On May 19, 2009, at 11:29 PM, Jose A. Ortega Ruiz wrote:
Hi Aziz,
Thanks for your interest in supporting Geiser, that's really great.
And
thanks a lot to Eduardo for the plug! :)
Abdulaziz Ghuloum <[email protected]>
writes:
I need more information about how he hooks things up. I presume that
geiser would be communicating with an inferior ikarus process (how?)
to ask about these things.
That's correct: Geiser spawns an interactive Scheme REPL and
communicates (using comint-mode for those in the know) by redirecting
the standard i/o. From the point of view of the Scheme process, it
looks
just like an interactive session, although a server Scheme process
providing a REPL through a socket is also supported.
Fine either way.
Then there's a simple protocol between the Elisp side and the REPL,
which is based on a set of procedures that are sent by Emacs to the
Scheme process standard input. Again, the latter sees them just as if
they were typed interactively. The values printed by the REPL to its
standard output are captured back by Emacs and interpreted (they're
formatted most of the time as alists).
Why are these procedures sent over the repl instead of being in
some library that the underlying implementation loads? Can't these
definitions interfere/collide with whatever the user is doing at
the repl? Ikarus does provide multiple interaction environments
so that, at the repl, you can create two interaction environments:
one for the user code and one for the geiser code is necessary.
Well, this is the basic idea. The actual implementation is a bit more
complex because of the necessity of capturing errors. Once the basic
communication procedures are working, I wrap them as evaluations
(i.e.,
using eval) which in turn are wrapped using the exception handling
mechanisim provided by the Scheme at hand (e.g. `with-handlers' in PLT
or `catch' in Guile), so that any error is captured and reported
back to
emacs. That way, the user can get nicely formatted diagnostics and we
preserved the integrity of the underlying Scheme process.
In R6RS, this can be done in the same way for all implementations.
This evaluation mechanism doubles as a way for the user to evaluate
any
form she wishes. A very nice thing about PLT and Guile (at least in my
opinion) is that they provide ways of evaluating form in the
context of
a module namespace. Even when these evaluation imply re-defining
exported identifiers: the system is aware of the new definitions,
transitively. For example, in PLT scheme one has
(eval <module-path> (module->namespace <module-path>))
This would be hard. Maybe the Ikarus back end would have to restrict
these forms to expressions, and no set!s to library variables.
One has also the notion of 'current module' in the REPL, which
complements nicely the above functionality: when you send an
expression
from a scheme buffer for evaluation, it gets evaluated in the buffer's
module or, if no module (library) is associated with the buffer, in
the
'current module'.
Doable, modulo the restriction above.
For what i've read about R6RS, this form of operation is actually not
possible in a strictly standard implementation (and actually i know
for
a fact that i'm using extensions beyond R6RS to implement it in
both PLT
and Guile). To me, that cuts half of the fun from a hypothetical
R6RS-Geiser, but there's still the other half, that can be uselful.
Well, there has to be *something* provided by the implementation. :-)
Theoretically, the implementation has more knowledge of things as it
is running than can be obtained by looking at the source alone. Plus,
implementations may have extensions that a "standard" system may not
know about.
I think it would be a great service to all R6RS implementors if it's
formally specified what the implementation needs to provide.
The procedures used by Geiser are listed, for instance, in
<http://git.hacks-galore.org/gitweb/jao?
p=geiser.git;a=blob;f=scheme/guile/geiser/emacs.scm;hb=HEAD>
Here's what they do:
- ge:eval (FORM MODULE)
Evaluates the given FORM (a sexp) in the context of MODULE
- ge:compile (FORM MODULE)
For systems providing JIT compilation; usually just an alias for
ge:eval.
In Ikarus, it's the other way around: eval is an alias for compile. :-)
But either way, I think the restriction I mentioned about will have
to be there. I don't think you need to worry about it much since you
would have to handle errors anyways.
- ge:macroexpand (FORM MODULE FULL?)
Expands the given FORM, recursively when FULL? is #t.
How is this used from Geiser?
- ge:compile-file (PATH)
- ge:load-file (PATH)
Load, or compile and load, a file (possibly containing a module
definition) given its path. In PLT and Guile this also means that
all
re-definitions are immediately seen by other modules importing the
(re)loaded one (as in ge:eval/ge:compile)
This is a hard one since the other modules too would have to be
recompiled and reloaded. And what about the values, say definitions
in the repl, that have been computed from the first definitions;
do they get re-evaluated too?
- ge:autodoc (IDENTIFIER MODULE)
When IDENTIFER (a symbol) is bound in MODULE, and the bound value
corresponds to a procedure, a list of arities is returned. Each
arity
consists of a list of required, optional and keyword arguments,
ideally with the argument names that were used during its
definition.
This is fine. I can do that.
- ge:completions (PREFIX MODULE)
Returns a list of all identifiers visible in MODULE's namespace
whose
string rep starts with PREFIX. Of course, here just returning a list
of all visible identifiers is enough; the filtering is easy to
implement.
Easy enough.
- ge:module-completions (PREFIX)
The same, but now we ask for a list of module paths starting with
PREFIX.
What's a module path?
- ge:symbol-location (SYMBOL MODULE)
Given a symbol (probably this should be called
ge:identifier-location), when it's bound in MODULE returns the full
path and line number of the source file where it's defined.
Returning
only the file's path is OK, provided that the original name of the
value is returned too.
Doable.
- ge:module-location (MODULE)
Given a module name, returns the full path to the file where's it's
implemented.
Good.
- ge:module-children (MODULE)
Returns a list of identifiers exported by the given module, as a
list
of symbols. Geiser processes this list to actually classify the
identifiers with the kind of value they're bound to (variables,
procedures, syntactic forms...).
Probably should be called "ge:module-exports", but fine.
- ge:callers (PROC-NAME MODULE)
- ge:callees (PROC-NAME MODULE)
Given a procedure defined in MODULE, produce a list of symbols
listing
its callers and callees, to some degree of approximation. Of course,
an exact list is not computable in general, but in Guile we're happy
providing partial list--it's better than nothing.
We'll do this in a second milestone.
- ge:generic-methods
- ge:symbol-documentation
These are specific to schemes providing OO a la CLOS and docstrings,
although if the system has any kind of built-in documentation
facility
that can be accessed programmatically, the latter hooks on that.
We don't have one yet.
These are actually high-level procedures, as seen by Emacs, which are
implemented in terms of more primitive ones (for one thing, they wrap
the actual results in the protocol understood by the Elisp side), but
i'd say that a proper interface for an R6RS 'standard' library
(describing the 'primitive procedures') can be readily derived from
them. It goes without saying that i'll help as much as i can towards
that goal, in case you feel that a substantial enough subset of the
procedures above is implementable in Ikarus or R6RS in general (if the
functionality is in there, i'm perfectly happy with implementing an
Ikarus-specific version of Geiser's backend, even if it's not
R6RS-compliant).
I'll look into geiser to see how it works and see how to hook it to
Ikarus before attempting to implement anything. I'll let you know
how far I get.
Thanks again for your interest!
Thank you!
Aziz,,,