Sujet : Re: History of lexical scope in Lisp
De : 433-929-6894 (at) *nospam* kylheku.com (Kaz Kylheku)
Groupes : comp.lang.lispDate : 16. Mar 2024, 18:54:05
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <20240316093534.951@kylheku.com>
References : 1 2 3 4
User-Agent : slrn/pre1.0.4-9 (Linux)
On 2024-03-16, Jeff Barnett <
jbb@notatt.com> wrote:
On 3/15/2024 4:26 PM, Lawrence D'Oliveiro wrote:
On Fri, 15 Mar 2024 16:05:37 -0600, Jeff Barnett wrote:
Once you have had the programming convenience of using dynamic scope,
you would miss it terribly in a lexical-only language, or worse, in
something like the old FORTRAN assembler-level scope. Another argument
that I have not seen debated but may be significant is that the CL Error
System -- the programming primitives and error class structures don't
make a lot of sense in lexical only. The error mechanisms like catch
have meanings that include phrases such as "while executing this. I'm
available to handle that".
Lexical binding has always been understood to apply to references to
definitions of identifiers. Exception handlers are dynamically-installed
(lexical-based exception handling doesn’t make any sense), nevertheless
the names of the defined exceptions being handled are still lexically-
bound.
This is how it works in every rationally-designed language.
>
First off, I believe that many if not most exception-related primitives
expand in terms of dynamic variables. And second, it would be amusing to
unwind to an environment that is lexically alive but execution dead-
think about restarting the contexts that were abandoned.
That might happen when you call a closure that was saved in such
an environment which was then dynamically abandoned.
When resuming a continuation captured in such an environment, though,
the dynamics have to appear intact.
(defun grandkid ()
(unwind-protect
(yield-from parent 'in-grandkid)
(put-line "returning from grandkid")))
(defun kid ()
(unwind-protect
(progn
(yield-from parent 'in-kid)
(grandkid))
(put-line "returning from kid")))
(defun parent ()
(unwind-protect
(progn
(yield-from parent 'in-parent)
(kid))
(put-line "returning from parent")))
(let ((fn (obtain (parent))))
(prinl 'a)
(prinl (call fn))
(prinl 'b)
(prinl (call fn))
(prinl 'c)
(prinl (call fn))
(prinl 'd)
(prinl (call fn))
(prinl 'e))
Run:
$ txr cont.tl
a
in-parent
b
in-kid
c
in-grandkid
d
returning from grandkid
returning from kid
returning from parent
nil
e
When (call fn) resumes the delimited continuation, the stack linkage is all
there: the unwind protect exit points, and all else. Dynamic variable bindings
also, though that is not apparent in this example. If you resume a continuation
into somwhere where *stdout* is redirected to a string, it's redirected to a
string via dynamic binding, that redirection is correctly observed.
To make this work, I didn't even give a second thought to the horrendously
impractical "dynamic-wind", which is dead on arrival from a feasability
point of view.
How it works is that the yield-from invocations perform an "absconding"
dynamic control transfer. An absconding control transfer is like a regular
dynamic control transfer, except it performs no unwinding.
1> (block foo (unwind-protect (sys:abscond-from foo 42) (prinl 'unwind)))
42
2> (block foo (unwind-protect (return-from foo 42) (prinl 'notprinted)))
notprinted
42
Abscond is a sharp knife, so it's kept in the system package, though
it is a documented feature.
If we resume the came continuation, we can repeatedly invoke the
same unwinding:
1> (block nil
(unwind-protect
(sys:capture-cont nil 'identity) ;; capture up to nil block
(prinl 'unwind)))
unwind
#<intrinsic fun: 1 param>
2> [*1 42] ;; call continuation
unwind
42
3> [*1 42]
unwind
42
4> [*1 42]
unwind
42
5> [*1 42]
unwind
42
(The yield stuff avoids this by updating the yield contexts to new
continuations which resume from the previously yielded point,
like a cooperative thread.)
-- TXR Programming Language: http://nongnu.org/txrCygnal: Cygwin Native Application Library: http://kylheku.com/cygnalMastodon: @Kazinator@mstdn.ca