更新:在DebianSid Docker容器中添加了一组额外的Guile3基准测试。
免责声明:我没有使用诡计。我几乎不知道。还有其他一些我更了解的Scheme实现。但由于Guile Emacs是一个充满希望和未经证实的主张的热门话题,我时不时地尝试一下。这里的所有“基准”结果都要谨慎对待,它们是在运行Arch Linux的Thinkpad X230t上使用Guile2.2.6和Emacs26.3创建的。
说到这里,来自#emacs[1]的Laurus提醒我,Guile Emacs之所以可能出现在第一位的原因之一是Guile的语言塔,Emacs Lisp就是其中一种受支持的语言。但这意味着什么呢?Emacs Lisp支持有多完整?它能用来做什么呢?这些和其他几个问题就是这篇博客文章的主题。
标准化编程语言有一个很大的好处,那就是基于一种规范,人们可以在任何时候怀疑事情应该如何表现时都可以参考。这允许开发几个具有自己独特优势和优势的竞争性实现。如果您遵循该标准,那么在不同实现之间切换并不困难,并且可以帮助消除错误,例如,当您使用不同的编译器编译C程序时。
如果您选择的语言决定放弃这种方法,并且正确的行为由它定义,那么事情就会变得相当困难,然而这并没有阻止人们为Python和Ruby等编程语言编写替代实现。自从Guile扩展到支持Emacs Lisp作为附加语言以来,Emacs Lisp就陷入了类似的境地。如果您的Guile版本足够新,您可以在REPL中评估简单的代码:
Scheme@(guile-user)>;(定义foo 1)Scheme@(guile-user)>;foo$1=1Scheme@(guile-user)>;,L elispHappy hacking with Emacs Lisp!要切换回,请键入`,L schema';.elisp@(Guile-User)>;(Defvar Bar 2)$2=barelisp@(Guile-User)>;bar$3=2。
到目前一切尚好。但是有多少Emacs Lisp是受支持的呢?不是很明显,许多常见的功能,比如消息和错误,都被解绑了。似乎也不可能对缓冲区或文件做任何事情。这极大地限制了用Emacs Lisp编写有用脚本的可能性[2]。确定到底支持什么的一种方法是参考源代码,但是当您可以通过编程测试它,从而创建一个可执行规范…时,有什么意思呢。
通常的测试方法让我失败了。是否通过带有读取字符串的标准输入读取测试输入?是否使用argv访问参数?是否正在使用INSERT-FILE-CONTENTS从文件中读取?是否使用getenv获取环境变量?所有这些都不受支持。至少您可以使用princ打印到stdout。相反,我采用了一种略有不同的方法,在Minimal Emacs环境中获取函数/变量列表[3],然后生成一个测试文件来检查它们的存在并打印测试摘要。以下是代码生成部分:
(deun printf(fmt&;rest args)(princ(Apply';format fmt args)(printf";elisp规范遵守测试(defvar通过0)(defvar失败0)(deful test-sym(Pred Sym)(if(Funcall Pred Sym)(setq通过(1+通过))(setq失败(1+失败)(deun test-un(Sym)(。bundp sym))\n\n";)(mapatom(lambda(ATOM)(WHEN(fbda(ATOM)(WHEN(FBOLDP ATOM)(printf";(test-un';%S)\n";ATOM)(WHEN(AND(NOT(关键字ATOM))(BONDP ATOM))(printf";(test-var';%S)\n";ATOM)(printf"。)(printf";(princ#34;通过:\";)\n";)(printf";(princ#34;(princ通过)\n";)(printf";(Terpri)\n";)(printf";\n";)(printf";(princ#34;失败:\";)\n";)(printf";(princ#34;)\n";)(printf";(princ#34;)\n";)(printf";(打印失败)\n";)(printf";(Terpri)\n";)。
假设它已保存为gen-elisp-spec.el,则使用emacs-q--Batch--script gen-elisp-spec.el>;elisp-spec.el生成可执行文件spec.el。下面是使用Emacs和Guile的测试会话:
[WASA@BOX~]$time emacs--q--Batch--script elisp-spec.el传递:9567失败:2emacs-q--Batch--script elisp-spec.el 0.10s用户0.02s系统99%cpu 0.117总计[WASA@BOX~]$time guile--language=elisp elisp-spec.el传递:137失败:9432guile--language=elisp elisp-spec.el。
这有点令人惊讶。我没想到Emacs自己的测试会失败,也没想到Guile会实现这一点。最令人惊讶的是测试的速度如此之快,我期待着任何能向我解释这一部分的人。下面是使用官方Debian Sid Docker映像与Emacs 26.3和Guile3.0.2进行的另一个测试:
root@d27668492764:/#time emacs--q--Batch--script elisp-spec.el传递:9108失败:2real 0m0.104suser 0m0.097ssys 0m0.007s root@d27668492764:/#time guile--language=elisp elisp-spec.el传递:137失败:8973 real 6m20.950suser 10m32.294ssys 0m7.。
这并不完全是一种进步。至少数字足够小,可以打印出令人不快的符号,对于Emacs,它是atom和printf(这污染了测试环境),出于谨慎,我冒昧地注释了列表:
;Binding let*;;Functions lambda Apply funcall;;评估评估加载eval-and-Compile eval-When-Compile;;Sequence AREF ASET make-Vector nth;;Sequence Progn Prog1;;Iteration Dolist When;;control flow if When Under cond;;短路或非;;显式非本地出口抛出捕获;;异常信号Condition-Case Unwind-Protect;;输入从微型缓冲区读取;;输出prin1-to-string打印princ-send-string-。搜索成员memql memq;;破坏性列表处理nverse setcar setcdr rplaca rplacd;;其他列表处理cons list make-list`mapcar mapc以反转长度追加;;symbols defconst defvar deun def宏get put fset setq setplist Symbol-Function Symbol-Name Symbol-plist Symbol-Value Intern make-Symbol fmakunbound makunbind引用函数;;plist plist-get plist-put lax-plist-get。比较器>;<;>;=<;=/==等式字符串-相等字符串=;;数值运算符+-*%;;杂项随机。
要做课本练习之外的任何事情,您需要定义额外的原语。Guile的模块/language/elisp/boot.el展示了如何将创可贴应用于前面的一些缺点:
(fset';/(@(Guile)/))(fset';read-string';read-string';read-from-mini buffer)(fset';prin1(@(Guile)write))(deFun print(Object)(Prin1 Object)(Terpri))。
您可以编写更多的代码来实现在Emacs之外使用Emacs Lisp脚本语言的目标,但是需要编写Scheme才能实现这一目标。为什么不直接使用方案呢?为什么要发明一个新的运行时呢?这一努力将可以与node.js为Chrome的JavaScriptengine所做的努力相媲美,只是销售宣传要弱得多。
我上面展示的内容几乎不足以引导其上的Emacs。Guile Emacs需要定制版本的Guile和Emacs,然后加载支持的Emacs Lisp文件来完成其余工作,还有更多的不兼容性,比如调用-交互-清除。扩展提供的基本规范以包含实际测试将有助于跟踪进度和可用性,如果核心开发人员同意并相信这一想法,它甚至可能提高GNU Emacs本身的整体质量。我在emacs-devel上搜索了一下之前关于这个话题的讨论,但只找到了关于Guile Emacs本身的简短描述,所以任何对这个话题有强烈兴趣的人,都可以在那里自由地开始讨论!
至于Guile Emacs本身,情况就更棘手了。上述存储库已有五年未被触及,RobinTempleton是两个Google Summer of Codeevents的唯一贡献者。尽管这项工作远未完成,但一名大学生在Guile的维护人员安迪·温戈的监督下成功地完成了这项工作,这是令人印象深刻的。进一步的进步需要有类似动机的个人参与Guile社区,并成为工作的一部分,就像其他自由软件项目一样。走一条像捐赠给其他开发者这样的捷径是令人沮丧的,但如果他们没有找到将这些钱转化为同等工作的方法,那么你赠送的东西和他们做的回报之间就没有什么联系。这也是一个值得讨论的话题,最好是与能够做出改变的人讨论。
劳拉斯也做了一些研究,你可以在#Guile频道上找到一个有趣的讨论:http://logs.guix.gnu.org/guile/2020-05-16.log。
至少您现在可以用更少的资源在Emacs Lisp中解决SICP:您有大数、缺省的词法作用域和TCO!
这并不完全正确,测试的是符号是否有其函数/值槽边界,其中可能包含其他内容,例如宏和关键字。
想想看,人们喜欢提倡Guile Emacs,认为它会让Emacs变得更快。虽然从长远来看,这可能是正确的,但它还远未接近这一点。这里希望Guile3能减轻…的一些痛苦