为什么Clojure比其他JVM语言更热插拔?

Hap*_*ace 5 jvm clojure hotswap read-eval-print-loop jrebel

我们可以在运行时几乎立即重新加载Clojure中的任何函数和/或变量.我们甚至可以改变方法签名.我们用Scala或Java做的最多就是使用速度慢,商业化和受限制的JRebel.允许Clojure如此互动的有什么区别?在Slack中读到这个,我发现了以下评论,但我希望了解更多.还希望进一步澄清澄清问题的论文/文章的链接(尽管不是必需的).

这主要是因为语言设置为可重新加载.Clojure为每个函数或顶级变量定义都有一个var间接,你可以改变它,所以你可以重新定义一个函数,同时保持你的环境的其余部分相同并继续

.

跟进 - 当函数名在代码中时有间接,但是对于一个长时间运行的函数,它将另一个函数作为参数(例如,你将处理函数传递给http服务器进程启动),你可以获得以下好处:手动var间接 - 通过传递#'处理程序而不是处理程序,否则你不会重新加载(没有重新启动那个arg的进程)

.

的种类

直接链接替换正在使用直接调用(已编辑)编译的var调用但是仍然存在var路径,并且仍然可以通过vars调用NEW代码

Ven*_*ius 12

您要问的关键在于Clojure如何识别函数并在运行时运行它们.首先,Clojure函数定义为vars,它们是JVM根类的Clojure名称Var.

Clojure的运行时维护了一个ConcurrentHashMap名为Namespaces.此映射具有Symbol键(名称空间名称)和Namespace值.每个Namespace都有一个AtomicReference'd Clojure map(称为"映射"),它是动态类型的,但基本上有Clojure Symbol键(局部变量名)和Var值.

当您调用Clojure函数时,它首先查找您引用的命名空间Namespaces,然后在该命名空间的映射中查找特定变量.这使得热加载代码变得微不足道 - 您需要做的就是<Symbol, Var>在给定命名空间的映射上设置一个新对.

为了更深层次,Clojure还保持对"框架"的意识(即线程或可能暂时在本地范围内重新定义变量的附加绑定).它们有自己的ThreadLocal存储空间,在其中一个存储器中找到的变量将用于代替当前存储在命名空间映射中的变量.


Clojure的方法是可行的,因为它不会尝试将函数存储为JVM函数,而是将Java对象本身保存在可以快速访问的映射中.

Clojure知道这些对象实际上是可调用的,通过检查它们是否满足函数接口(IFn).IFn通过具有Invoke方法满足对象.这用于许多非常聪明的目的,并解释了为什么许多Clojure的核心数据结构(地图,向量,关键字等)都可以作为函数调用.