我有时发现在Clojure中定义简化的arity版本的函数很方便,这些函数会返回一个部分函数,例如
(defn prefix
([pre string]
(str pre ":" string))
([pre]
(fn [string]
(prefix pre string))))
这意味着你可以这样做:
(prefix "foo" 78979)
=> "foo:78979"
((prefix "foo") 78979)
=> "foo:78979"
这似乎非常Haskell-ish并且避免了partial
创建部分函数的需要。
但它在Lisp中被认为是良好的编码风格/ API设计吗?
答案 0 :(得分:9)
使用partial
创建一个curried函数是基于明确更好(在大多数情况下:)的概念。我发现这个概念在Clojure,Python等动态类型语言中更适用/使用可能是因为缺少类型签名/静态类型,使事情变得更明确。
答案 1 :(得分:7)
默认情况下,在函数调用的语言中,函数调用也是curry。当一个人写道
(f a b c)
,语言将其解释为(((f a) b) c)
。在Clojure中并非如此。
我认为在默认情况下不调用调用的环境中创建curried函数会产生概念上的不匹配 - 这种结构可能会引起读者混淆(我的意思是代码的人类读者)。
如果你的函数有两个以上的参数,那么这个定义会很快变得难看。假设一个函数有4个参数。要完全模拟currying调用,当有人第一次传递2个参数然后剩下的两个参数时,你需要处理像((f a b) c d)
这样的情况。在这种情况下,双参数函数的重载版本需要返回一个重载函数,该函数的行为会有所不同,具体取决于它是获得1还是2个参数。我想有可能用宏来自动化,但仍然。
此外,您无法定义默认参数和& rest
构造。
答案 2 :(得分:6)
partial
,因为这样可以清楚地知道发生了什么。
我不知道它是“好”还是“坏”的编码风格,但我以前从未在现有的Clojure代码中看过这种风格,我可以想象使用API的人会期望(prefix "foo" 78979)
和(prefix "foo")
返回相同类型的对象。
为了明确两个功能之间的区别,你可以做一些类似的事情:
(defn prefix [pre string]
(str pre ":" string))
(defn prefix-fn [pre]
(fn [string]
(prefix pre string)))
(prefix "foo" 78979) ; => "foo:78979"
((prefix-fn "foo") 78979) ; => "foo:78979"