可以使clojure充满活力吗?

Joh*_*den 10 clojure

在clojure 1.1中,所有调用都是动态的,这意味着您可以在REPL中重新定义一个函数,它将自动包含在正在运行的程序中.这对像dotrace这样的东西也很好.

在clojure 1.2中,许多调用似乎是静态链接的,如果我想替换一个函数,有时候,我必须找到所有被调用的地方并将#'放在它们前面.

更糟糕的是,我无法预测我需要做什么.

是否有可能回到动态链接的旧默认值?也许如果你需要额外的速度iota,你可以重新开启生产应用程序,但对于开发我更喜欢1.1的行为.

我希望有某种编译器选项,比如*warn-on-reflection*.

编辑:

我对发生的事感到困惑.更具体地说,这里有两个功能.我更喜欢第二种的行为.我怎么能让第一个表现得像第二个,我相信它曾经在1.1中做过?

user> (clojure-version)
"1.2.0"

user> (defn factorial[n] (if (< n 2) n (* n (factorial (dec n)))))
#'user/factorial

user> (require 'clojure.contrib.trace)
user> (clojure.contrib.trace/dotrace (factorial) (factorial 10))
TRACE t1670: (factorial 10)
TRACE t1670: => 3628800

user> (defn factorial[n] (if (< n 2) n (* n (#'factorial (dec n)))))
#'user/factorial
user> (clojure.contrib.trace/dotrace (factorial) (factorial 10))
TRACE t1681: (factorial 10)
TRACE t1682: |    (factorial 9)
TRACE t1683: |    |    (factorial 8)
TRACE t1684: |    |    |    (factorial 7)
TRACE t1685: |    |    |    |    (factorial 6)
TRACE t1686: |    |    |    |    |    (factorial 5)
TRACE t1687: |    |    |    |    |    |    (factorial 4)
TRACE t1688: |    |    |    |    |    |    |    (factorial 3)
TRACE t1689: |    |    |    |    |    |    |    |    (factorial 2)
TRACE t1690: |    |    |    |    |    |    |    |    |    (factorial 1)
TRACE t1690: |    |    |    |    |    |    |    |    |    => 1
TRACE t1689: |    |    |    |    |    |    |    |    => 2
TRACE t1688: |    |    |    |    |    |    |    => 6
TRACE t1687: |    |    |    |    |    |    => 24
TRACE t1686: |    |    |    |    |    => 120
TRACE t1685: |    |    |    |    => 720
TRACE t1684: |    |    |    => 5040
TRACE t1683: |    |    => 40320
TRACE t1682: |    => 362880
TRACE t1681: => 3628800
3628800
Run Code Online (Sandbox Code Playgroud)

编辑(对整个问题和标题的更改):

Joost在下面指出,这里实际发生的是因子的自我调用正在被优化掉.我不明白为什么会这样做,因为你不能在没有吹掉堆栈的情况下做那么多递归的自我调用,但它解释了观察到的行为.也许这与匿名自我调用有关.

我的问题的原因是我试图写http://www.learningclojure.com/2011/03/hello-web-dynamic-compojure-web.html,我对我所拥有的地方数量感到恼火输入#'以获得我期望的行为.那和dotrace让我觉得一般的动态行为已经消失了,在某些地方工作的动态重新定义必须用一些聪明的黑客来完成.

回想起来,这似乎是一个奇怪的结论,但现在我只是困惑(哪个更好!).这有什么参考吗?我希望有一个关于什么时候会起作用以及何时起作用的一般理论.

Stu*_*rra 11

Clojure中的所有内容都是完全动态的,但您必须注意何时使用Var以及何时使用函数,该函数是该Var 的当前.

在你的第一个例子中:

(defn factorial [n] (if (< n 2) n (* n (factorial (dec n)))))
Run Code Online (Sandbox Code Playgroud)

factorial符号被解析为Var #'user/factorial,然后由编译器对其进行评估以获得其当前值,即编译函数.编译函数时,此评估仅发生一次.第factorial一个例子中是定义函数时Var 的#'user/factorial.

在第二个例子中:

(defn factorial [n] (if (< n 2) n (* n (#'factorial (dec n)))))
Run Code Online (Sandbox Code Playgroud)

你明确要求Var #'user/factorial.调用Var与取消引用Var并调用其(函数)值具有相同的效果.此示例可以更明确地编写为:

(defn factorial [n] (if (< n 2) n (* n ((deref (var factorial)) (dec n)))))
Run Code Online (Sandbox Code Playgroud)

clojure.contrib.trace/dotrace宏(这是我写的,以前的年龄)使用binding暂时重新绑定一个VAR为不同的值.它不会改变任何函数的定义.相反,它创建一个新函数,调用原始函数并打印跟踪线,然后将该函数绑定到Var.

在您的第一个示例中,由于原始函数是使用函数的编译的factorial,dotrace因此无效.在第二个示例中,factorial函数的每次调用都会查找#'user/factorialVar 的当前值,因此每次调用都会看到由其创建的备用绑定dotrace.


dno*_*len 9

因此人们不会对这些问题感到困惑,我正在解释与Web开发相关的"问题".

这是Ring not Clojure的限制(实际上它是Java Jetty库的限制).您始终可以正常重新定义功能.但是,无法重新定义为Jetty服务器进程提供的处理程序.您的功能正在更新,但Jetty服务器无法看到这些更新.在这种情况下,提供var作为处理程序是可行的.

但请注意var不是真正的处理程序.必须将一个AbstractHandler赋予Jetty服务器,因此Ring使用代理创建一个关闭处理程序的代理.这就是为了使处理程序动态更新的原因,它需要是var而不是fn.

  • 这种解决方法(可以这么说)起作用,因为Var足以将任何`.invoke`调用转发给它包含的东西. (2认同)

Joo*_*aat 8

我觉得你错了.在clojure 1.2中,您当然可以重新定义函数,调用代码将调用新定义.在1.3中看起来这可能会有所改变,但1.3还没有固定.


Art*_*ldt 8

在Clojure-1.3中,您还可以在运行时重新定义函数(从而更改根绑定),这将与1.2和1.1相同.但是,您需要使用动态标记动态反弹的变量binding.这种突破性变化提供了

  • 显着的速度提升
  • 允许绑定通过pmap工作
  • 完全值得,因为99%的变量永远不会反弹