Clojure let 与多元性
Clojure let vs multi-arity
像函数式思考,但在 clojure 中,哪个更好,性能更好,对 JVM 的负担更轻
(defn- on-message
([options ch {:keys [headers delivery-tag]} ^bytes payload ^CompanyProto$Company$Builder company]
(check-id company)
(save company options)
(basic/ack ch delivery-tag))
([options ch ^PersistentHashMap kwargs ^bytes payload]
(on-message options
ch
kwargs
payload
(-> (CompanyProto$Company/newBuilder)
(.mergeFrom payload)))))
或
(defn- on-message [options ch {:keys [headers delivery-tag] ^bytes payload}]
(let [company (-> (CompanyProto$Company/newBuilder) (.mergeFrom payload))]
(check-id company)
(save company options)
(basic/ack ch delivery-tag)))
如果您使用 Java 互操作进行开发,请将 *warn-on-reflection*
设置为 true
。如果有警告,你需要用类型提示来消除它。然后你得到"perfomatic and less heavy to the JVM"代码。
P.S。具有可变性、简单 JVM 类型和非检查溢出的数值计算是另一回事,如果是你的情况,你也需要检查一下。
您的直接问题的答案是,函数调用中不会引入任何开销,仅仅是因为该函数恰好具有多个参数。在 Clojure 中,所有的函数调用都被编译成一个方法调用。调用的确切方法取决于参数的数量——就 JVM 而言,每个元数都被编译为不同的方法。在编译时确定调用哪个方法,因此在 运行 时没有开销。
也就是说,进行嵌套函数调用会产生一些开销。具体来说,每次调用都会使调用堆栈增长一个常数,直到该调用 returns。但是,这种开销很小,在这种情况下不太可能对性能产生可衡量的影响。
@ToniVanhala I did with let first, but a friend said that let is bad
and not functional (he is a erlang developer), I'm new in functional
langs... the other thing that he said is that is weird that clojure is
not stackless
这似乎是真正的问题。所以值得解决这个问题。
let
只是一个允许我们将值绑定到变量的表达式。这些绑定是不可变的。此外,众所周知,let
可以作为 lambda 的宏抽象实现(或者,在 Clojure 的词汇表中,fn
)。所以 let
肯定没有什么使它成为 "not functional".
对于 Clojure 不是无堆栈的,也没有任何 "weird" 或令人困惑的地方。 Clojure 是一种 JVM 语言,调用堆栈深深嵌入 JVM 的抽象计算模型中。虽然有一些方法可以解决这个问题(例如,continuation-passing 样式),但这些方法要么需要放弃深度 JVM 互操作 and/or,但要付出性能代价。 Clojure首先是一门务实的语言,这是务实的让步。
此外,Clojure(作为 Lisp)确实是一种语言框架,您可以使用它来打造自己想要的语言。您的语言可能会做出不同的权衡。例如,您的语言可能是 "stackless". Or it might have first-class continuations (disclaimer: I am the author of pulley.cps
). Or it might have a completely different paradigm(即逻辑与功能)。但它也可以非常简单。使用 Clojure,您可以选择要支付的费用。
具有讽刺意味的是,您朋友关于代码无法正常运行的说法是正确的。然而,这并不是因为let
。相反,几乎所有 除了 let
都不起作用。例如,(save company options)
显然是一个副作用操作。不过,这并不一定意味着您的代码不好——即使是最纯粹的函数式程序,如果要具有任何实用价值,在某些时候也必须与现实世界交互。
像函数式思考,但在 clojure 中,哪个更好,性能更好,对 JVM 的负担更轻
(defn- on-message
([options ch {:keys [headers delivery-tag]} ^bytes payload ^CompanyProto$Company$Builder company]
(check-id company)
(save company options)
(basic/ack ch delivery-tag))
([options ch ^PersistentHashMap kwargs ^bytes payload]
(on-message options
ch
kwargs
payload
(-> (CompanyProto$Company/newBuilder)
(.mergeFrom payload)))))
或
(defn- on-message [options ch {:keys [headers delivery-tag] ^bytes payload}]
(let [company (-> (CompanyProto$Company/newBuilder) (.mergeFrom payload))]
(check-id company)
(save company options)
(basic/ack ch delivery-tag)))
如果您使用 Java 互操作进行开发,请将 *warn-on-reflection*
设置为 true
。如果有警告,你需要用类型提示来消除它。然后你得到"perfomatic and less heavy to the JVM"代码。
P.S。具有可变性、简单 JVM 类型和非检查溢出的数值计算是另一回事,如果是你的情况,你也需要检查一下。
您的直接问题的答案是,函数调用中不会引入任何开销,仅仅是因为该函数恰好具有多个参数。在 Clojure 中,所有的函数调用都被编译成一个方法调用。调用的确切方法取决于参数的数量——就 JVM 而言,每个元数都被编译为不同的方法。在编译时确定调用哪个方法,因此在 运行 时没有开销。
也就是说,进行嵌套函数调用会产生一些开销。具体来说,每次调用都会使调用堆栈增长一个常数,直到该调用 returns。但是,这种开销很小,在这种情况下不太可能对性能产生可衡量的影响。
@ToniVanhala I did with let first, but a friend said that let is bad and not functional (he is a erlang developer), I'm new in functional langs... the other thing that he said is that is weird that clojure is not stackless
这似乎是真正的问题。所以值得解决这个问题。
let
只是一个允许我们将值绑定到变量的表达式。这些绑定是不可变的。此外,众所周知,let
可以作为 lambda 的宏抽象实现(或者,在 Clojure 的词汇表中,fn
)。所以 let
肯定没有什么使它成为 "not functional".
对于 Clojure 不是无堆栈的,也没有任何 "weird" 或令人困惑的地方。 Clojure 是一种 JVM 语言,调用堆栈深深嵌入 JVM 的抽象计算模型中。虽然有一些方法可以解决这个问题(例如,continuation-passing 样式),但这些方法要么需要放弃深度 JVM 互操作 and/or,但要付出性能代价。 Clojure首先是一门务实的语言,这是务实的让步。
此外,Clojure(作为 Lisp)确实是一种语言框架,您可以使用它来打造自己想要的语言。您的语言可能会做出不同的权衡。例如,您的语言可能是 "stackless". Or it might have first-class continuations (disclaimer: I am the author of pulley.cps
). Or it might have a completely different paradigm(即逻辑与功能)。但它也可以非常简单。使用 Clojure,您可以选择要支付的费用。
具有讽刺意味的是,您朋友关于代码无法正常运行的说法是正确的。然而,这并不是因为let
。相反,几乎所有 除了 let
都不起作用。例如,(save company options)
显然是一个副作用操作。不过,这并不一定意味着您的代码不好——即使是最纯粹的函数式程序,如果要具有任何实用价值,在某些时候也必须与现实世界交互。