This grew out of requests from discussions around the whitepaper for an introduction to Scheme’s concepts, and was originally an appendix, but it grew enough that we decided it should be its own thing! (It’ll be removed from being an appendix when I publish the next release of the whitepaper now that we have the separate primer.)
Your feedback, as always, is welcome. This one’s fairly public already, so feel free to spread it around if you like!
Apparently it is bad form to make multiple reply-posts, so I’ve consolidated my prior edits into the previous two posts, and will add any remaining edits to this one…
12. Scheme in Scheme
“Supplying only two operators, + and - , we are able to compute the Fibonacci sequence:” should be “Supplying only two operators, + and = , we are able to compute the Fibonacci sequence:”
In the section explaining the implementation of lambda, you say:
the procedure we return recursively calls evaluate against the body expression of the lambda we are matching against, but with a newly extended environment
This is the point where a choice is made between lexical and dynamic scoping. I think it would be worth pointing out that the environment where lambda is evaluated is captured and used in place of the dynamic environment where the procedure is invoked. Perhaps with a corresponding back-reference in the discussion of procedure application, pointing out that the arguments are evaluated in a different environment than the body of the lambda.
Thanks for all the good catches! I’m on vacation so allegedly I’m not even reading this let alone replying to it or lord forbid fixing things
Yes that’s well put. I thought a fun follow-up document to this, some day, might be to show off (borrowing from SICP’s Variations on a Scheme) how with just a couple of small tweaks one can switch to dynamic scope or make it prolog-like or etc. Maybe at some point in the future…
Thanks again @dale.schumacher ! I integrated almost all of your suggestions, but I didn’t do the alignment on the substitution method one… it’s a sensible decision but aesthetically I liked the way it was, whether that’s a good choice or not.
Also you might like the new footnote I added:
[fn:lexical-scope-happens-here] This is where the choice is made
between lexical and dynamic scoping. The environment which is
extended is not the environment of the caller, but the environment of
the previous expression in which this lambda was defined... this is is
how we achieve the functionality described in the =closures= section.
In a sense, "lexical scope" is "scope is defined by capturing where it
is written" and is capability secure, is explicit, and preserves the
"if you don't have it, you can't use it" property. "Dynamic scope" is
instead "scope is defined by the invoker" (and their parent invokers,
all the way up the stack of current invocations) and is implicit, but
also ambient including in the ambient authority sense, making dynamic
scope suffer more serious security issues (and not coincidentally,
more strongly resemble access control list systems).