Differences between revisions 1 and 8 (spanning 7 versions)
Revision 1 as of 2011-02-10 16:09:07
Size: 245
Editor: BtTempleton
Comment: links to mailing list
Revision 8 as of 2011-07-04 04:56:58
Size: 7886
Editor: BtTempleton
Comment: lambda list oddities
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
BtTempleton has submitted [[http://www.google-melange.com/gsoc/proposal/review/google/gsoc2011/bpt/1|a proposal]] to complete eir work on Guile-Elisp during Summer 2011.

== 2010 GSoC work ==
Line 4: Line 8:
 * [[http://article.gmane.org/gmane.lisp.guile.devel/11546|Final update]]

== 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)

----
CategoryGuileEmacs

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)


CategoryGuileEmacs

GuileElisp (last edited 2011-07-10 22:14:41 by BtTempleton)