持续 x 与 (fn [& _] x) 有何不同?

3
我正在使用多方法来根据项目运行的“模式”提供不同的功能(它是一个yada api服务器,应该能够在:dev:prod等模式下运行)。
我正在使用mount/defstate来提供一个关键字:
(defstate mode :start :dev)

当我使用(constantly mode)进行调度时,会出现错误,但是当我使用(fn [& _] mode)进行调度时似乎可以正常工作。
这两种形式不是相同的吗?或者它们在评估方式(或时间)上有一些微妙的差异吗?

2
请添加错误。 - cfrick
2个回答

5
mount中,如果您还没有启动状态,则它们的值是DerefableState对象。
通过调用constantly,您首先评估mode的值,然后调用具有该值的constantly函数。这意味着当您调用constantly的结果时,它将始终返回constantly的参数,尽管您已经改变了它。如果在调用constantly之前未启动状态,则它将存储DerefableObject
另一方面,使用(fn [& _] mode),每次调用该函数时都会评估mode var的值。如果您还没有启动状态,则它也会返回DerefableState,但如果您已经启动,则结果将是预期的关键字。
一个简单的解决方案是将调度函数也放在一个状态中。
(defstate dispatch :start (constantly state))

谢谢,解释得非常好。可以这样说吗,constantly形式在编译时关闭mode,因为它存在于编译时,而fn形式在运行时评估mode - Jeremy Field
1
在运行时,constantly 会捕获 mode,而不是编译时。 - erdos
我尝试按照您的建议将dispatch放入defstate中,但是在(defmulti fn-name dispatch)行中出现了mount.core.DerefableState cannot be cast to clojure.lang.IFn。我想我明白为什么会发生这种情况。我是否误解了您的建议? - Jeremy Field
嗯,这取决于你如何使用dispatch,但可能与上面相同:在启动Mount之前使用其值。也许尝试使用#'dispatch而不是dispatch - erdos
我选择了一个半解决方法: https://gist.github.com/jdf-id-au/419c83f119bad7b4981a168ab6d37b78 - Jeremy Field

3
我认为,作为另一种解释,您可以使用副作用来突出差异。

比较:

(def const-f (constantly (println "Hello!")))
Hello!
=> #'user/const-f

(def fn-f (fn [] (println "World!")))
=> #'user/fn-f

执行第一个def会导致打印Hello!,因为constantly的主体立即评估。然而,第二个def不打印任何内容,因为fn的主体没有被评估。

但在调用它们时:

(const-f)
=> nil ; Prints nothing. Just evaluates to what println returned

(fn-f)
World! ; Prints now,
=> nil ;  then returns what println evaluates to

constantly 不是宏,因此必须先评估其参数。然而,fn 是宏,因此在评估其参数之前运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接