Discussion:
[fonc] maru
Aaron Burrow
2013-11-10 11:20:40 UTC
Permalink
Ian, or anyone else familiar with maru internals.

What is the purpose of the decode(...) function? I see that it is doing a
substitution of symbols for Fixed and Subr where appropriate; why is this
not be handled in subr_eval_symbol(...)?

Is decode(...) preparing something for eval(...) that would be hard to do
from within eval(...)?

Continuing, we see that decode(...) must make accommodations for special
forms like lambda and let. In both cases it prepends bindings to the
environment that are set to nil. Presumably the bindings are intended for
the recursion that comes from enlist(...); at which point the same process
repeats for each element of the lambda/let. Why are these bindings
relevant to the recursive call?

This is with respect to the minimal maru available in the google code
repository; although maru-2.4 appears to have a very similar but more
developed decode(...) mechanism.

Thanks,
Aaron
Aaron Burrow
2013-11-10 11:39:11 UTC
Permalink
:%s/decode/encode/g
Post by Aaron Burrow
Ian, or anyone else familiar with maru internals.
What is the purpose of the decode(...) function? I see that it is doing a
substitution of symbols for Fixed and Subr where appropriate; why is this
not be handled in subr_eval_symbol(...)?
Is decode(...) preparing something for eval(...) that would be hard to do
from within eval(...)?
Continuing, we see that decode(...) must make accommodations for special
forms like lambda and let. In both cases it prepends bindings to the
environment that are set to nil. Presumably the bindings are intended for
the recursion that comes from enlist(...); at which point the same process
repeats for each element of the lambda/let. Why are these bindings
relevant to the recursive call?
This is with respect to the minimal maru available in the google code
repository; although maru-2.4 appears to have a very similar but more
developed decode(...) mechanism.
Thanks,
Aaron
Ian Piumarta
2013-11-10 22:18:25 UTC
Permalink
Aaron,
What is the purpose of the decode(...) function? I see that it is doing a substitution of symbols for Fixed and Subr where appropriate; why is this not be handled in subr_eval_symbol(...)?
encode, en(code)list, etc., try to increase performance by finding which binding a name refers to at function definition time, removing the need to traverse a long association list for every variable access during evaluation. For globals they replace a name with a direct reference to its binding's value location. For locals they simulate the stack of local contexts that will be created at run time and encode variable accesses as nesting level (number of contexts to 'pop') plus offset (into the context you arrived at).

For globals that refer to primitives or functions there is the opportunity to short-circuit the call to its final destination without ever indirecting through the binding at runtime. This changes the usual semantics of define and set, in which changing the value of a definition during execution normally changes the value seen by all references to it. When functions are short-circuited, all references to a function that were made in function definitions completed before changing the value with 'set' will continue to refer to the original value. Neither semantic is obviously more correct than the other. The former is 'classic' Lisp and the latter has some appealing encapsulation properties.

The contexts and bindings that are created during this process are never used for execution.
Is decode(...) preparing something for eval(...) that would be hard to do from within eval(...)?
It is preparing direct references to associations for globals and to level+offset bindings for locals in LIFO contexts, removing (in most cases) the need for eval to traverse an alist during execution.
Continuing, we see that decode(...) must make accommodations for special forms like lambda and let. In both cases it prepends bindings to the environment that are set to nil. Presumably the bindings are intended for the recursion that comes from enlist(...); at which point the same process repeats for each element of the lambda/let. Why are these bindings relevant to the recursive call?
This is tracking the nesting of contexts that are created by let and by the parameter list of lambda so that encode can convert names that refer to local variables into their level+offset location at runtime.
This is with respect to the minimal maru available in the google code repository; although maru-2.4 appears to have a very similar but more developed decode(...) mechanism.
The latest version of Maru removes the mechanism entirely because adding a small runtime cache of symbol-to-binding mappings to the association list lookup is far simpler and performs in almost all cases just as well as the complicated pre-encoded version.

Hope that helps.

Regards,
Ian

Loading...