自指语言:一种可以访问自身解释器的Lisp方言
Autology: A Lisp with access to its own interpreter

原始链接: https://github.com/Kimbsy/autology

Autology是一种独特的Lisp方言,它使用Clojure实现,通过将解释器作为绑定到变量`*i*`的可变数据结构来提供运行时元编程能力。与在编译时操作的传统Lisp宏不同,Autology允许在程序执行期间动态修改语言的行为。 其核心思想是将`*i*`重新绑定到修改后的解释器版本。例如,可以通过扩展解释器的`:atl/eval-list`部分来处理`λ`(lambda)形式来添加函数。这允许进行即时语言扩展。 虽然由于性能开销的原因,Autology并非天生实用,但它能够探索动态语言重写,甚至能够实现诸如切换评估策略或在Autology环境中嵌入元循环解释器等功能。它提供了一种新颖的运行时元编程方法,允许进行即席语言适配,这超出了典型编译时宏系统的能力。

Hacker News上的一篇文章讨论了名为“Autology”的Lisp实现(可在GitHub上找到),该项目似乎致力于探索动态生成嵌入式虚拟机。 评论者sargstuff好奇这种方法与fork Lisp系统调用、复制列表或对fork的Lisp进程应用diff(可能用于并发支持)相比如何,并开玩笑地抱怨该名称与现有品牌相似。 另一位评论者timonoko对使用自身创建语言的想法表示惊讶,并质疑在汇编方面花费的时间。然而,他们也承认其局限性,可能暗示了自指系统的复杂性。

原文

A hot take on Lisp metaprogramming, Autology is a Lisp with access to its own interpreter.

Autology is a functional interpreted Lisp language, written in Clojure.

The Autology interpreter function is defined as a data structure which is available to the Autology program, it is bound to the variable *i*.

Whenever an expression needs to be evaluated, Autology will retrieve the current value of *i* from the current lexically scoped execution environment and use it to evaluate the expression.

By binding new values of *i* we can modify the behaviour of the Autology language while our program is running.

Mainly for fun and to explore a neat idea.

Autology is not a particularly useful language as-is, its very slow and resource intensive.

Macros are the normal tool for Lisp languages to modify their syntax or behaviour, but these are normally only available at compile time. Autology allows dynamic rewriting of the language at runtime.

Even other non-Lisp languages are able to define some form of Domain Specific Language to allow programmers to express problems more clearly, however these are generally quite restrictive in scope. Autology allows full ad-hoc re-writing of the syntax of the language, as well as defining new features or removing existing ones.

Well to start with you might like to add functions to the language. Autology doesn't have them by default, but we can rebind the *i* symbol to a data structure representing a new interpreter function, one that does have functions.

(bind (;; grab a copy of the `:atl/eval-list` section of the
       ;; interpreter which is responsible for evaluating lists.
       original (get-marker *i* :atl/eval-list)

       ;; define a case test+body for use when the list expression
       ;; starts with our function special form, in this case `λ`.
       λ-form (qu (λ (let [[_λ params body] e]
                       (fn [& values]
                         (autology.core/evaluate
                          body
                          (reduce (fn [acc-env [s v]]
                                    (assoc acc-env s v))
                                  env
                                  (zipmap params values)))))))

       ;; rebind `*i*` to be a new interpreter with the
       ;; `:atl/eval-list` section replaced with a version that
       ;; includes our lambda handling special form.
       *i* (replace-marker *i* :atl/eval-list
                           (list :atl/eval-list
                                 (concat (butlast original)
                                         λ-form
                                         (list (last original)))))

       ;; We can now immediately define functions since the
       ;; interpreter will have already been updated to evaluate the
       ;; remaining bindings like this one.
       double (λ (n)
                 (+ n n)))

      ;; Finally we can invoke our new function!
      (double (double (double (double (double 1.3125))))))

After that, pretty much anything you want!

  • Why not switch form applicative order evaluation to normal order evaluation?
  • Maybe modify the language so it uses a continuation passing style?
  • Define a metacircular Lisp inside your program which has access to it's own interpreter as well at the Autology interpreter.
联系我们 contact @ memedata.com