Guilemacs – Emacs with Guile Scheme

栏目: IT技术 · 发布时间: 4年前

内容简介:For the former contents of this page which were for a large part concerned with the history of all efforts to integrate Guile and Emacs, seeGuileEmacsHistory.To try Guile Emacs in its current state, seeGuileEmacsBuild.Guile-based Emacs is a branch of GNU E

For the former contents of this page which were for a large part concerned with the history of all efforts to integrate Guile and Emacs, seeGuileEmacsHistory.

To try Guile Emacs in its current state, seeGuileEmacsBuild.

Overview

Guile-based Emacs is a branch of GNU Emacs that replaces Emacs’s ownEmacsLisp engine with the Elisp compiler ofGuile. It does not attempt to remove Elisp, and instead aims to become the canonical GNU Emacs of the future by being fully backwards compatible. It’s best to call it “GuileEmacs” or “Guile/Emacs” or such to make it clear that it’s still GNU Emacs.

One advantage is that Elisp will potentially execute faster, because Guile uses a compiler tower with many optimization passes and ultimately compiles to Guile VM bytecode, which is more efficient than current Elisp bytecode. Also, Guile has native JIT since version 3.0. In the future, Guile is likely to implement some forms of AOT compilation.

A second advantage is that it will be easier to implement some additional language features for Elisp which the Guile compiler tower and VM are capable of, like a full numeric tower with not only infinite-sized integers (which GNU Emacs 27.0.50 already has) but also exact rational numbers, imaginary numbers, etc.), record types (like an improved defstruct), CLOS-like OOP, an FFI, composable continuations, a module system, hygienic macros, multiple-value returns, and threads (GNU Emacs has a limited support for concurrency – LISP threads – since version 26.1).

A third advantage is all Guile APIs/libraries becoming available to Elisp code, no matter what language they’re implemented in, because different languages on the Guile VM can inter-operate quite well, especially if they’re both a Lisp. C-implemented functions (“subrs” in Elisp terminology), Elisp functions, Scheme procedures, etc. all compile to the same “procedure” data type, which may appear in Elisp symbols’ function-slots, be bound to Scheme variables, and are otherwise first-class objects in both environments which can be funcalled or applied explicitly or by the language’s normal syntactic way of calling functions. Similarly, other data types are unified between the languages; Elisp integers and exact Scheme integers, inexact Scheme numbers and Elisp floats, Elisp cons cells and Scheme pairs, symbols, etc. are the same data type across the languages. (Strings are an exception though; see below.) Therefore one can generally use a library written in another language as if it were written in the same language.

Lastly, it will become possible to write Emacs extensions in Scheme instead of Elisp, where it’s possible to load the Elisp function and variable namespaces as modules (say with a prefix for each so common names like ‘car’ aren’t overwritten).

Current State

The latest publicly available state ofGuileEmacs can be found at BT Templeton's Emacs repository and in their Guile repository . The latter has in the meanwhile made it into the wip-elisp branch of guile’s main git repository. The “wip” branches of both represent the latest state ofGuileEmacs and both are necessary to test it. (Build the wip branch of the Guile repo first, then build the wip branch of the Emacs repo with that.)

As of the end ofGoogleSummerOfCode 2014, the Elisp engine of Emacs is fully replaced with that of Guile, and most things Just Work™. Some performance regressions remain, especially with programs using dynamic scope. SeeGuileEmacsTodo.

Dynamic scoping

Elisp defvars are dynamically scoped, as well as let-bound variables when lexical scope isn’t enabled for an Elisp file. These use the same machinery as parameter objects in Scheme. Currently these are not as efficient as they should be, and the presence of buffer-local variables and other weirdities of Elisp binding mechanisms cause additional headaches to Guile in implementing Elisp, which so far leads to a very big performance regression. This will hopefully be addressed shortly.

So can I script Emacs in Scheme already?!

For the Scheme fans who dislike Elisp:

Guilemacs – Emacs with Guile Scheme

Reproduced below for convenience:

;; This buffer is for notes you don't want to save, and for Lisp evaluation.
;; If you want to create a file, visit that file with C-x C-f,
;; then enter the text in that file's own buffer.

(load-library "scheme")
(scheme-interaction-mode)
;; that was Elisp; now we're in Scheme:

(use-modules ((elisp-symbols) #:prefix ev-) ;variables
             ((elisp-functions) #:prefix ef-)
             ((elisp-plists) #:prefix ep-)
             )

(ef-message (ef-symbol-name 'cool!))
"cool!"

Note: You’ll need to use C-j for evaluation, not C-x C-e or C-M-x .

Long-term issues

There seem no long-term issues for pure Elisp users, but the following challenges will face those who want to mix Elisp with Scheme:

Strings

Elisp strings are encoded in an extended UTF-8 format, whereas Guile just uses libunistring. For this reason Elisp strings will for starters be a separate data type from other Guile strings (say coming from Scheme code). This poses no problems to pure Elisp code, but limits one’s ability to seamlessly use Scheme libraries from Elisp, and to write Emacs extensions in Scheme, because one needs to explicitly convert back and forth between Elisp and Scheme strings.

Nil, false, and the empty list

Scheme uses separate objects for the false Boolean and empty list (AKA null); Elisp uses nil for both. This poses a challenge in the interoperation of the languages. Guile approaches the issue by internally supporting all three objects (false, null, and nil) but making Elisp and Scheme treat them in such ways that the programmer generally doesn’t notice.

In Elisp, all three objects are (almost) completely indistinguishable, even with ‘eq’ , meaning one can ignore the whole issue and all Elisp code continues working as-is. The one way in which the false and null objects might behave different is when ‘print’ ing them; it is not set in stone yet what behavior they should have: they might print something other than “nil”, so if they make a round trip like Scheme → Elisp → print → read → Elisp → Scheme, the Scheme code loses no information; or the loss of information could be accepted, making them print “nil” when printed from Elisp code.

In Scheme, nil (which has the syntax #nil ) can take the role of both false and null (in ‘if’ , ‘not’ , null? , etc.), but none of the three are equal? (let alone eqv? or eq? ) to each other. That’s because if x and y are equal, and y and z are equal, then x and z must be equal too, but we can’t make false and null equal in Scheme. To elaborate through some code examples: (if #nil 0 1) yields 1, (and (not #nil) (null? #nil)) yields true, and (cons 'foo #nil) yields (foo) . This means that everything is fine so long as the user uses ‘if’ , ‘not’ , null? , etc. instead of comparing equality to literal objects.

A noteworthy algorithm that won’t work in Scheme without modification is one to find the common tail of two lists that share structure. This algorithm cuts two lists to the same length, then walks them in parallel and does an eq? check on every pair of nodes. Normally this will end at least when the () at the end of both lists is reached (meaning their only common tail is the empty list), but in Scheme code messing with mixed Elisp data one has to separately check for the (and (null? list1-tail) (null? list2-tail)) case at every node (actually testing one of the two is enough because they have the same length), or else one has to canonicalize the lists’s terminating value in advance.

As an additional weirdity, the nil and true objects are also symbols in Elisp, since after all they’re ‘nil’ and ‘t’ . They are not symbols in Scheme though, despite being the same objects. The Elisp ‘symbolp’ function and Scheme symbol? procedure simply disagree here, among others which deal with symbols.

Long story short: Elisp is generally unaffected, but don’t freak out if your code prints out some “false” or “null” object where you expected “nil”. If you write Scheme code interacting with Elisp data, make sure to use ‘if’ / ‘not’ / null? /etc. instead of comparing equality to #f or '() . Don’t assume that lists end with equal objects either, which was previously guaranteed because they all ended in null.

Macros

While procedures are shared between the languages, macros are more difficult. Elisp macros are just procedures of course so they could in principle work as unhygienic Scheme macros, and Elisp code goes through the same intermediate language as Scheme so perhaps hygienic Scheme macros could work on Elisp.

The former would probably not work sensibly though, since Elisp function and variable names appearing in the macro output would be inserted into Scheme code where they’re undefined.

The latter might just work. Scheme’s hygienic macros don’t output Scheme code (raw sexprs); they output code in an intermediate language (IL) that can make direct references to bindings in any modules, as well as represent all code concepts that Guile supports in the lower levels. Scheme and Elisp are already both translated to this IL before further compilation or interpretation, so using hygienic macros defined in Scheme on Elisp code would not have any issues; it would simply separately compile (to IL) the Scheme body of the macro template and the Elisp code snippet input to the macro, and merge these IL snippets which both reference the correct Scheme and Elisp variable and function bindings. Even identifier inputs to the macro which it binds with ‘let’ , like in (syntax-rules () ((with-foo foo body ...) (let ((foo whatever)) body ...))) , should conceptually work ( lexically bind ‘foo’ for the Elisp code in ‘body’ ).

It should also be possible to implement a hygienic macro definition form for Elisp, though ‘defmacro’ itself can probably not be made hygienic so using all the currently existing Elisp macros from Scheme remains problematic. They will probably need to be slowly reimplemented as hygienic macros defined in Elisp or Scheme.

Concurrency

Guile supports concurrency essentially via POSIX threads. In Guile-Emacs it is possible to launch any number of threads executing Scheme, but this doesn’t solve the problem that Emacs is full of thread-unsafe data structures and global resources, so programming an Emacs extension using concurrent Scheme code might prove to be full of unexpected roadblocks and pitholes. Some Elisp functions even abuse the single-threaded nature of Emacs, mutating global resources then mutating them back to give the illusion of referential transparency, so nonchalant usage of Elisp APIs from concurrent code remains problematic.


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning Web App Development

Learning Web App Development

Semmy Purewal / O'Reilly Media / 2014-3-3 / USD 29.99

Grasp the fundamentals of web application development by building a simple database-backed app from scratch, using HTML, JavaScript, and other open source tools. Through hands-on tutorials, this pract......一起来看看 《Learning Web App Development》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具