定义减少的arity部分函数

mik*_*era 8 lisp functional-programming clojure

我有时发现在Clojure中定义简化的arity版本的函数很方便,这些函数返回一个部分函数,​​例如

(defn prefix 
  ([pre string] 
    (str pre ":" string))

  ([pre] 
    (fn [string]
      (prefix pre string)))) 
Run Code Online (Sandbox Code Playgroud)

这意味着您可以执行以下任一操作:

(prefix "foo" 78979)
=> "foo:78979"

((prefix "foo") 78979)
=> "foo:78979"
Run Code Online (Sandbox Code Playgroud)

这看起来很Haskell-ish并且避免了partial创建部分函数的需要.

但它在Lisp中被认为是良好的编码风格/ API设计吗?

Ank*_*kur 9

使用partial创造咖喱功能是基于概念明确越好(在大多数情况下:)).我发现这个概念在Clojure,Python等动态类型语言中更适用/使用可能是因为缺少类型签名/静态类型,使事情更明确.


Raf*_*ird 7

在默认情况下函数被调用的语言中,函数调用也是curry.当一个人写作时(f a b c),语言将 其解释为(((f a) b) c).在Clojure中并非如此.

我相信在默认情况下不调用调用的环境中创建curried函数会产生概念上的不匹配 - 这种结构可能会引起读者混淆(我的意思是代码的人类读者).

如果你的函数有超过2个参数,那么定义会很快变得难看.假设一个函数有4个参数.要完全模拟currying调用,你需要处理一些情况,例如((f a b) c d)当有人第一次传递2个参数然后传递其余两个参数时.在这种情况下,双参数函数的重载版本需要返回一个重载函数,该函数的行为会有所不同,具体取决于它是获得1还是2个参数.我想有可能用宏来自动化,但仍然如此.

此外,你杀死了定义默认参数和& rest构造的可能性.


Ger*_*ert 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"
Run Code Online (Sandbox Code Playgroud)

  • "我以前从未在现有的Clojure代码中看到过这种风格" - 这就是Hickey新推出的Reducer库. (2认同)