BtTempleton has submitted a proposal to complete eir work on Guile-Elisp during Summer 2011.
2010 GSoC work
Lisp-1 / Lisp-2
> Currently Elisp symbols are Scheme symbols, and their value and function > cell contents are looked up in submodules of (language elisp runtime). > This works well enough for now, but it will get quite ugly if Elisp is > ever integrated with Guile's module system to any degree. Every module > containing Elisp function bindings might need to have a 'function-cell' > submodule, for example; an uninterned symbol could be used to avoid name > clashes, but it would still pollute the namespace. It would be cleaner > to maintain multiple hash tables in every module, and then e.g. (@ > (emacs) set-buffer function) could refer to the function binding. > > But here's another alternative: Guile already has function and plist > slots in symbols, so clearly it's okay to waste a few bytes for features > nobody uses. (: Why not keep the extra cells in variable objects, or in > a new type of variable object? (I call this option "big variables" or > "bigvars".) Guile's double cells are just the right size; there's enough > room for a function cell as well as a pointer to a properties object, > which would contain the plist cell and implementation-internal > annotations. I've implemented the (trivial) libguile side of this, but > at least Tree-IL, the module system, and the VM would also need to be > modified to support this, if it's even a good idea. Hmmm. Yes, I think this is a good idea. Does it make sense to be a subtype of variables -- that is, with the same tc7, but with a flag in the first word? Then we could make Guile's variables all be treated as having a value binding already. Perhaps the new variable-function-ref / toplevel-function-ref / etc opcodes (you are adding them, yes? :) should return the value if it's a narrow variable holding a procedure. We would need toplevel-value-define / toplevel-function-define as well, to ensure that the binding is a wide variable.
Scope
> I've been reading old CL proposals regarding global lexical bindings, > and I think I now understand why this can't work in Guile. Previously I > wasn't considering the existence of a *global* environment, only lexical > and dynamic bindings, and hence I wasn't understanding the bindings > lookup order required for this. Scheme only allows LG lookup -- it looks > for variables in the lexical environment, then falls back to the global > environment. > > Globally-bound Elisp and CL variables are pervasively special, and > cannot be lexically bound. Globally-bound CL functions are pervasively > lexical (IIUC), and cannot be dynamically bound. Elisp bindings have > only global bindings, and cannot be lexically or dynamically bound. But > for Elisp function bindings, we need a globally-bound variable which can > be bound both lexically and dynamically. To allow dynamic shadowing of a > global lexical (but not pervasively lexical!) variable, the > implementation must do LDG lookup -- checking in the dynamic environment > after the lexical environment but before the global environment, which > is distinct from both the lexical and dynamic environments. But Scheme > doesn't *have* a true dynamic environment; it only simulates it by > storing fluids (i.e., indices into local dynamic state objects) in the > global environment, which of course means Guile Elisp can't have a > dynamic binding for a variable while retaining the global binding. > Therefore, it appears to me that Guile needs a real dynamic environment > if Elisp function bindings are to be non-special without breaking cl.el > FLET.
Aliases
> Elisp has both defalias and defvaralias. My first thought was to assign > the same variable object to multiple names in an obarray; that works for > the submodule-based implementation of symbol cells currently used, but > not for the bigvars implementation. It would be useful to have a special > object like SCM_UNDEFINED to mark cells to be redirected, but if that's > not practical, redirected cells can remain SCM_UNBOUND, redirection > information information can be stored in the property cell, references > to aliases can be processed in an error handler, and Elisp setq can > check the property cell for every assignment. I think I'll implement a > proper def(var)alias next; subr.el and other basic libraries load with > defalias = fset, but Emacs implements buffer- and frame-local variables > using a similar technique so this will make it easier to implement that > when it becomes necessary.
Nil
I'll have to go through my notes, but I'm not sure #nil is the best solution
Adding VM instructions
Procedure for adding nil?' and not-nil?' VM instructions for Elisp conditionals:
1. Add the new instructions to "vm-i-scheme.c". This is all that needs to be done for it to be usable in GLIL.
2. Test it in the REPL:
scheme@(guile-user)> ,L glil Happy hacking with Guile Lowlevel Intermediate Language (GLIL)! To switch back, type `,L scheme'. glil@(guile-user)> (program () (std-prelude 0 0 #f) (const #f) (call nil? 1) (call return 1)) $1 = #t glil@(guile-user)> (program () (std-prelude 0 0 #f) (const ()) (call nil? 1) (call return 1)) $2 = #t glil@(guile-user)> (program () (std-prelude 0 0 #f) (const #nil) (call nil? 1) (call return 1)) $3 = #t glil@(guile-user)> (program () (std-prelude 0 0 #f) (const #t) (call nil? 1) (call return 1)) $4 = #f glil@(guile-user)> (program () (std-prelude 0 0 #f) (const #f) (call not-nil? 1) (call return 1)) $5 = #f glil@(guile-user)> (program () (std-prelude 0 0 #f) (const ()) (call not-nil? 1) (call return 1)) $6 = #f glil@(guile-user)> (program () (std-prelude 0 0 #f) (const #nil) (call not-nil? 1) (call return 1)) $7 = #f glil@(guile-user)> (program () (std-prelude 0 0 #f) (const #t) (call not-nil? 1) (call return 1)) $8 = #t
3. Add a Scheme or C definition in the (guile) module.
4. Add the procedure to the appropriate lists in "language/tree-il/primitives.scm".
5. Compile an application of the procedure to GLIL. It should be recognized as a primitive:
scheme@(guile-user)> (compile '(nil? #f) #:to 'glil) $1 = #<glil (program () (std-prelude 0 0 #f) (label :LCASE99) (const #f) (call nil? 1) (call return 1))>
6. That's it!
There may be more work for certain types of instructions. For `nil?', I added support for a new type of conditional, which involved adding a branch instruction and support for it in the Tree-IL compiler.
@@ Should VM instructions be renumbered?
Miscellaneous
Trying to compile the invalid Tree-IL program (begin) used to lead to weird errors
See the "elisp glimpses" email for some notes on a package system for Elisp
The macros eval-when-compile and eval-and-compile work everywhere, unlike cl:eval-when. But macro definitions only have compile-time effects at the top level in Elisp -- the current behavior (2011-06-20) is incorrect
I originally thought that &rest alone at the end of a lambda list meant to ignore extra arguments, but in fact &rest and &optional are ignored entirely if there isn't a symbol following them. For example, (lambda (x &rest) ...) = (lambda (x) ...). (I don't think it's necessary to do the same in GuileElisp)
In Emacs, although (eval-and-compile (setq lexical-binding t)) has the expected effect with the byte-compiler, the evaluator only checks the local value before evaluating a buffer or loading a file.
Emacs's lexical-let interacts oddly with lambda:
(setq x 0) (defun dynx () x) (lexical-let ((x 1)) ((lambda (x) (list x (dynx))) 2)) ;; => (1 2) in Emacs, (2 0) in Guile (lexical-let ((x 1)) (let ((x 2)) (list x (dynx)))) ;; => (2 0) in both implementations
In Emacs, the lambda expression establishes a dynamic binding but x still refers to the lexical binding. The let example works as you'd expect because the CL package macroexpands it to a letf of symbol-value. However, the CL package's macroexpander more or less ignores the lambda expression's argument list even though x is a symbol macro. This situation is probably quite rare, so I'm going to leave it as a known incompatibility.