Clojure如何编译';它在一个REPL中运行吗?

Clojure如何编译';它在一个REPL中运行吗?,clojure,compilation,read-eval-print-loop,Clojure,Compilation,Read Eval Print Loop,诚实的问题。根据Russ Olsen得到Clojure,我知道以下几点: (1) Clojure代码在运行前编译为JVM字节码 (2) Clojure代码可以在REPL中运行,几乎是即时反馈,REPL使用函数(read)和(eval)或类似的函数 因此,似乎Clojure代码到JVM字节码的编译必须在REPL期间的某个时间点进行,可能是在(读取)阶段或之后不久 但这是一个模糊的心理图景,我想澄清一下 例如,最好知道在REPL中何时实际编译代码,如何将编译创建的数据存储在RAM中,然后由(eval

诚实的问题。根据Russ Olsen得到Clojure,我知道以下几点:

(1) Clojure代码在运行前编译为JVM字节码

(2) Clojure代码可以在REPL中运行,几乎是即时反馈,REPL使用函数(read)和(eval)或类似的函数

因此,似乎Clojure代码到JVM字节码的编译必须在REPL期间的某个时间点进行,可能是在(读取)阶段或之后不久

但这是一个模糊的心理图景,我想澄清一下

例如,最好知道在REPL中何时实际编译代码,如何将编译创建的数据存储在RAM中,然后由(eval)访问,以及其间或之后发生的任何重要步骤

换句话说,我想更详细地了解香肠是如何制作的:

Clojure如何编译REPL中运行的代码?


(优点:这与Clojure从非REPL源(例如Leiningen项目)编译代码有什么不同?

读者使用字符并生成Clojure数据结构(列表、向量、符号等)。读取阶段对JVM字节码绝对一无所知。这是eval阶段的一部分:编译器使用这些数据结构并生成JVM字节码

运行REPL时,该字节码存储在DynamicClassLoader中—所有JVM类都必须由某个类加载器定义,而DynamicClassLoader是Clojure创建的,以允许从Clojure数据结构动态定义类


编译到类文件时,相同的字节码只需以.class文件的形式写入磁盘,然后可能打包到jar中。

一个简短的测试揭示了答案:

(ns tst.demo.core
(:使用tupelo.core tupelo.test))
(带跟踪的宏)
[标签和表格]
(println“***运行宏***标记:”标记)
`(做
(println~tag:enter)
(让[结果#(do~@forms)]
(println~标签:离开)
结果#)
(宠爱
(新行)
(println:回答
(带跟踪:add-5)
(+ 2 3)))
(新行)
(println:使用eval
(评估
(引用(tst.demo.core/with-trace:最终答案
(clojure.core/inc 41(()()()))
其中打印:

> lein clean ; lein test

*** running macro ***   tag:   :add-5

lein test _bootstrap

-------------------------------
   Clojure 1.10.1    Java 14
-------------------------------

lein test tst.demo.core

:add-5 :enter
:add-5 :leave
:answer 5

*** running macro ***   tag:   :the-ultimate-answer
:the-ultimate-answer :enter
:the-ultimate-answer :leave
:using-eval 42
因此,我们看到
eval
不仅对函数进行“评估”,还对宏进行“评估”


更新


@amalloy是正确的,我忘了引用发送到
eval
中的代码,因此带跟踪的宏
是在编译时而不是运行时计算的。在这个版本中,发送到
eval
的代码被引用,因此它只是一个由2个符号、1个关键字和一个整数组成的嵌套列表。现在,
eval
看到宏,运行它生成实际代码,然后编译并运行宏输出。

由于编译严格在(eval)期间进行,我认为宏在(读取)之后(eval)之前展开是正确的吗?宏展开是编译器执行的一个步骤。如果要评估窗体,并检测到该窗体表示宏的用法,则会在继续之前展开宏。