一些语言实现已经有很长一段时间了,特别是很多Lisp变种和Smalltalk.
Lisp将标识符作为数据结构,称为符号.可以重新分配这些符号,并在运行时查找它们.这个原则叫做后期绑定.符号命名函数和变量.
另外,Lisp实现或者在运行时具有解释器甚至编译器.界面是功能EVAL和COMPILE.另外还有一个函数LOAD,它允许加载源代码和编译代码.
接下来,像Common Lisp这样的语言有一个对象系统,它允许更改类层次结构,类本身,可以添加/更新/删除方法,并将这些更改传播到现有对象.因此,面向对象的软件和代码可以自行更新.使用元对象协议,甚至可以在运行时重新编程对象系统.
Lisp实现然后可以垃圾收集删除的代码也很重要.这样,运行的Lisp不会因为代码被替换而在运行时大小上增长.
Lisp通常还有一个错误系统,可以从错误中恢复,并允许从调试器中替换有缺陷的代码.
这里有很多很好的答案,我不确定我可以改进其中任何一个,但我想添加一些关于 Clojure 和 Java 的评论。
首先,Clojure 是用 Java 编写的,因此您绝对可以用 Java 构建实时编码环境。只需将 Clojure 视为一种特定风格的实时编码环境即可。
基本上,Clojure 中的实时编码通过 main.clj 中的 read 函数和 core.clj 中的 eval 函数(github 存储库中的 src/clj/clojure/main.clj 和 src/clj/clojure/core.clj)进行工作。您读取表单并将它们传递给 eval,它调用 clojure.lang.Compiler(存储库中的 src/jvm/clojure/lang/Compiler.java)。
Compiler.java 使用 ASM 库(此处为 ASM 网站,此处为文档)将 Clojure 表单转换为 JVM 字节码。我不确定 Clojure 使用哪个版本的 ASM 库。然后,必须将该字节码(字节数组 => byte[] 字节码是 Compiler 类的成员,该类最终将保存由 clojure.asm.ClassWriter 类通过 ClassWriter#toByteArray 生成的字节)转换为类并链接到正在运行的进程。
一旦将类表示为字节数组,就需要获取 java.lang.ClassLoader,调用 DefineClass 将这些字节转换为类,然后将生成的类传递给 ClassLoader 的解析方法将其链接到 Java 运行时。这基本上就是定义一个新函数时发生的情况,您可以在 Compiler$FnExpr 中看到编译器的内部结构,它是为函数表达式生成字节码的内部类。
Clojure 的作用远不止于此,例如它处理命名空间和符号驻留的方式。我不完全确定它是如何解决标准类加载器不会用该类的新版本替换链接类这一事实的,但我怀疑这与类的命名方式以及符号的驻留方式有关。Clojure还定义了自己的ClassLoader,某个clojure.lang.DynamicClassLoader,它继承自java.net.URLClassLoader,因此可能与它有关;我不知道。
最后,所有的部分都可以在类加载器和字节码生成器之间进行 Java 实时编码。您只需提供一种将表单输入到正在运行的实例中、评估表单并将它们链接起来的方法。
希望这能让大家对这个问题有更多的了解。