• History of lexical scope in Lisp

    From Paul Rubin@21:1/5 to All on Fri Mar 15 11:55:32 2024
    Does anyone know when lexical scope started appearing in Lisp? Not
    counting Scheme, did it exist in predecessors of Common Lisp? Was it
    used much? Is it really true that Common Lisp had both lexical and
    dynamic scope in order to support older code that was written relying on dynamic scope and was too hard to convert?

    Thanks. This topic came up in the Forth group.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jeff Barnett@21:1/5 to Paul Rubin on Fri Mar 15 16:05:37 2024
    On 3/15/2024 12:55 PM, Paul Rubin wrote:
    Does anyone know when lexical scope started appearing in Lisp? Not
    counting Scheme, did it exist in predecessors of Common Lisp? Was it
    used much? Is it really true that Common Lisp had both lexical and
    dynamic scope in order to support older code that was written relying on dynamic scope and was too hard to convert?

    Thanks. This topic came up in the Forth group.

    I'll comment on only a part of your questions, namely whether CL had
    dynamic scope only for compatibility? The view that I and many others
    had was that compatibility made it a must AND:

    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".

    The availability of such fine grained control structures makes many
    typical Lisp non-hierarchical reasoning* programs possible. I believe
    that the early and middle AI communities were drawn to Lisp-like
    development tools because of these possibilities.

    * By non-hierarchical reasoning, I'm thinking of Simon's "almost
    decomposable systems" ideas.
    --
    Jeff Barnett

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Fri Mar 15 23:04:59 2024
    On 2024-03-15, Lawrence D'Oliveiro <ldo@nz.invalid> 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.

    Names of exceptions (conditions) are not lexically bound in Common Lisp.

    They are pervasive, similarly to class names.

    Lexically scoped exception handling does make sense, during those
    sections of the handling when a lexical scope isn't being abandoned.

    If the termination point of an exception is in lexical scope of the
    origin, then the case can be made for handling the entire episode
    lexically, including the unwinding.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Jeff Barnett on Fri Mar 15 22:26:57 2024
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Robert Brown on Sat Mar 16 01:03:52 2024
    On Fri, 15 Mar 2024 20:23:51 -0400, Robert Brown wrote:

    For instance, the Guice dependency injection framework
    for Java implements @RequestScoped settings, which are dynamically
    scoped and thread local.

    Can’t find any mention of “dynamic” scoping in the docs <https://github.com/google/guice/wiki/Scopes>. “@RequestScoped” just
    seems to mean what it says: the scope is per-request.

    Guice wouldn't need that feature if Java
    natively supported dynamically-scoped variables.

    It seems to me the effect can be achieved more simply by allowing
    access to instance methods as first-class objects, as you can do in
    Python, e.g.

    request_inst = Request(...)
    # class instantiation
    request_meth = request_inst.meth
    # method access

    But as usual, Java insists on doing things in a complicated way...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Paul Rubin on Sat Mar 16 02:37:51 2024
    On 2024-03-15, Paul Rubin <no.email@nospam.invalid> wrote:
    Does anyone know when lexical scope started appearing in Lisp? Not
    counting Scheme, did it exist in predecessors of Common Lisp? Was it
    used much? Is it really true that Common Lisp had both lexical and
    dynamic scope in order to support older code that was written relying on dynamic scope and was too hard to convert?

    Global variables are dynamically scoped in Common Lisp; the standard
    language doesn't provide for global variables that are not dynamically
    scoped: "dynamic variable", "global variable" and "top-level variable"
    refer to the same thing. Common Lisp's lexical scope design therefore
    isn't "total" in a sense; it doesn't provide top-level variables.

    New Common Lisp code continues to make use of dynamic scope.

    Important predefined variables such as the standard streams (e.g. *standard-output*) are dynamic variables. By locally binding
    *standard-output* we can redirect standard output to a different
    stream.

    In multithreaded Common Lisp implementations, dynamically scoped
    variables provide thread-local storage (as an extension to the
    language). If each thread can have its own independent binding of a
    dynamic variable, it means that the symbol serves as a key
    to a thread-specific storage location.

    There are good reasons to use dynamic variables in newly written Lisp
    code.

    For instance, a context handle (e.g. database handle) that rarely
    changes and that is passed around in numerous API calls can be a special variable, and then it doesn't have to be repated in all those calls.
    Yet if it has to take on different values, there is a clean way to do
    that.

    The aforementioned standard streams are such context handles. For
    instance when we call (write-strin "abc"), the default destination is *standard-output*; it need not be mentioned, if desired. The function
    will use the current value of the dynamic variable *standard-output*.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jeff Barnett@21:1/5 to All on Sat Mar 16 00:46:21 2024
    T24gMy8xNS8yMDI0IDQ6MjYgUE0sIExhd3JlbmNlIEQnT2xpdmVpcm8gd3JvdGU6DQo+IE9u IEZyaSwgMTUgTWFyIDIwMjQgMTY6MDU6MzcgLTA2MDAsIEplZmYgQmFybmV0dCB3cm90ZToN Cj4gDQo+PiBPbmNlIHlvdSBoYXZlIGhhZCB0aGUgcHJvZ3JhbW1pbmcgY29udmVuaWVuY2Ug b2YgdXNpbmcgZHluYW1pYyBzY29wZSwNCj4+IHlvdSB3b3VsZCBtaXNzIGl0IHRlcnJpYmx5 IGluIGEgbGV4aWNhbC1vbmx5IGxhbmd1YWdlLCBvciB3b3JzZSwgaW4NCj4+IHNvbWV0aGlu ZyBsaWtlIHRoZSBvbGQgRk9SVFJBTiBhc3NlbWJsZXItbGV2ZWwgc2NvcGUuIEFub3RoZXIg YXJndW1lbnQNCj4+IHRoYXQgSSBoYXZlIG5vdCBzZWVuIGRlYmF0ZWQgYnV0IG1heSBiZSBz aWduaWZpY2FudCBpcyB0aGF0IHRoZSBDTCBFcnJvcg0KPj4gU3lzdGVtIC0tIHRoZSBwcm9n cmFtbWluZyBwcmltaXRpdmVzIGFuZCBlcnJvciBjbGFzcyBzdHJ1Y3R1cmVzIGRvbid0DQo+ PiBtYWtlIGEgbG90IG9mIHNlbnNlIGluIGxleGljYWwgb25seS4gVGhlIGVycm9yIG1lY2hh bmlzbXMgbGlrZSBjYXRjaA0KPj4gaGF2ZSBtZWFuaW5ncyB0aGF0IGluY2x1ZGUgcGhyYXNl cyBzdWNoIGFzICJ3aGlsZSBleGVjdXRpbmcgdGhpcy4gSSdtDQo+PiBhdmFpbGFibGUgdG8g aGFuZGxlIHRoYXQiLg0KPiANCj4gTGV4aWNhbCBiaW5kaW5nIGhhcyBhbHdheXMgYmVlbiB1 bmRlcnN0b29kIHRvIGFwcGx5IHRvIHJlZmVyZW5jZXMgdG8NCj4gZGVmaW5pdGlvbnMgb2Yg aWRlbnRpZmllcnMuIEV4Y2VwdGlvbiBoYW5kbGVycyBhcmUgZHluYW1pY2FsbHktaW5zdGFs bGVkDQo+IChsZXhpY2FsLWJhc2VkIGV4Y2VwdGlvbiBoYW5kbGluZyBkb2VzbuKAmXQgbWFr ZSBhbnkgc2Vuc2UpLCBuZXZlcnRoZWxlc3MNCj4gdGhlIG5hbWVzIG9mIHRoZSBkZWZpbmVk IGV4Y2VwdGlvbnMgYmVpbmcgaGFuZGxlZCBhcmUgc3RpbGwgbGV4aWNhbGx5LQ0KPiBib3Vu ZC4NCj4gDQo+IFRoaXMgaXMgaG93IGl0IHdvcmtzIGluIGV2ZXJ5IHJhdGlvbmFsbHktZGVz aWduZWQgbGFuZ3VhZ2UuDQoNCkZpcnN0IG9mZiwgSSBiZWxpZXZlIHRoYXQgbWFueSBpZiBu b3QgbW9zdCBleGNlcHRpb24tcmVsYXRlZCBwcmltaXRpdmVzIA0KZXhwYW5kIGluIHRlcm1z IG9mIGR5bmFtaWMgdmFyaWFibGVzLiBBbmQgc2Vjb25kLCBpdCB3b3VsZCBiZSBhbXVzaW5n IHRvIA0KdW53aW5kIHRvIGFuIGVudmlyb25tZW50IHRoYXQgaXMgbGV4aWNhbGx5IGFsaXZl IGJ1dCBleGVjdXRpb24gZGVhZC0gDQp0aGluayBhYm91dCByZXN0YXJ0aW5nIHRoZSBjb250 ZXh0cyB0aGF0IHdlcmUgYWJhbmRvbmVkLg0KLS0gDQpKZWZmIEJhcm5ldHQNCg0K

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Jeff Barnett on Sat Mar 16 07:27:57 2024
    On Sat, 16 Mar 2024 00:46:21 -0600, Jeff Barnett wrote:

    First off, I believe that many if not most exception-related primitives expand in terms of dynamic variables.

    Consider the following Python example:

    class MyException1(Exception) :
    pass
    #end MyException1

    class MyException2(Exception) :
    pass
    #end MyException2

    def func1() :
    raise MyException1
    #end func1

    def func2() :
    raise MyException2
    #end func2

    def func3() :
    class MyException1(Exception) :
    pass
    #end MyException1

    try :
    func1()
    except MyException1 :
    # will never get here
    print("caught MyException1 in func3")
    #end try
    #end func3

    def func4() :
    try :
    func2()
    except MyException2 :
    print("caught MyException2 in func4")
    #end try
    #end func4

    for f in (func1, func2, func3, func4) :
    try :
    print("* call %s" % f.__name__)
    f()
    except MyException1 :
    print("caught MyException1 at top level")
    except MyException2 :
    print("caught MyException2 at top level")
    #end try
    #end for

    Here is the output it produces:

    * call func1
    caught MyException1 at top level
    * call func2
    caught MyException2 at top level
    * call func3
    caught MyException1 at top level
    * call func4
    caught MyException2 in func4

    func4 shows how the search for a handler is based on dynamic execution
    nesting. func3 shows how exception matching is based on lexical
    scoping.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Jeff Barnett on Sat Mar 16 16:54:05 2024
    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.

    (block foo (unwind-protect (sys:abscond-from foo 42) (prinl 'unwind)))
    42
    (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/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Robert Brown on Sat Mar 16 21:59:10 2024
    On Sat, 16 Mar 2024 11:32:46 -0400, Robert Brown wrote:

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Fri, 15 Mar 2024 20:23:51 -0400, Robert Brown wrote:

    For instance, the Guice dependency injection framework for Java
    implements @RequestScoped settings, which are dynamically scoped and
    thread local.

    Can’t find any mention of “dynamic” scoping in the docs
    <https://github.com/google/guice/wiki/Scopes>. “@RequestScoped” just
    seems to mean what it says: the scope is per-request.

    The use of thread local state to store request scoped Guice bindings is discussed in this thread:

    https://groups.google.com/g/google-guice/c/gDonVGO1wjY/m/_SSi4CcQAJYJ

    You mean the commenter who says “I'm not saying this is
    often (or even ever) a good thing to do; I'm just illustrating the technique”?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jeff Barnett@21:1/5 to Lawrence D'Oliveiro on Sat Mar 16 23:13:20 2024
    On 3/16/2024 1:27 AM, Lawrence D'Oliveiro wrote:
    On Sat, 16 Mar 2024 00:46:21 -0600, Jeff Barnett wrote:

    First off, I believe that many if not most exception-related primitives
    expand in terms of dynamic variables.

    Consider the following Python example:

    class MyException1(Exception) :
    pass
    #end MyException1

    class MyException2(Exception) :
    pass
    #end MyException2

    def func1() :
    raise MyException1
    #end func1

    def func2() :
    raise MyException2
    #end func2

    def func3() :
    class MyException1(Exception) :
    pass
    #end MyException1

    try :
    func1()
    except MyException1 :
    # will never get here
    print("caught MyException1 in func3")
    #end try
    #end func3

    def func4() :
    try :
    func2()
    except MyException2 :
    print("caught MyException2 in func4")
    #end try
    #end func4

    for f in (func1, func2, func3, func4) :
    try :
    print("* call %s" % f.__name__)
    f()
    except MyException1 :
    print("caught MyException1 at top level")
    except MyException2 :
    print("caught MyException2 at top level")
    #end try
    #end for

    Here is the output it produces:

    * call func1
    caught MyException1 at top level
    * call func2
    caught MyException2 at top level
    * call func3
    caught MyException1 at top level
    * call func4
    caught MyException2 in func4

    func4 shows how the search for a handler is based on dynamic execution nesting. func3 shows how exception matching is based on lexical
    scoping.

    Unfortunately, I don't speak Python. However if your point is that you
    can build "simulations" of one sort of scoping out of primitives for
    another sort, I agree.

    In fact Doug Pintar and I invented and used a language called CRISP (https://www.softwarepreservation.org/projects/LISP/crisp_ibm370_sdc) in
    the 1070s. It was used to implement some interesting speech
    understanding systems as part of the DARPA SUR Program. CRISP Allowed
    local and dynamic bindings and stack segments including bindings,
    catches, and return points where represented. There where primitives
    that allowed these stack segments to be elements of three types of
    trees: binding trees where dynamic bindings not located in one segment
    where sought in the parent segment; resume trees where segment
    executions did a return that was not handled in the current segment were reflected to the parent segment; and handler trees where a throw not
    handled by the executing segment was continued into the parent segment.
    The "highest" node in all trees was the global context. The parent-child relation in all trees could be rearranged under program control. Out of
    the related primitives, e.g., eval-in(exp, segment) you could construct
    pretty much whatever binding, control. and error handling structure that
    you needed. The reason for this baroque approach is that when we started constructing speech systems we had absolutely no idea what program
    organization structures would be convenient.

    The above was inspired by some papers using the rubric "spaghetti
    stacks". Don't remember the authors but Danny Bobrow might have been implicated.
    --
    Jeff Barnett

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Jeff Barnett on Sun Mar 17 05:37:52 2024
    On Sat, 16 Mar 2024 23:13:20 -0600, Jeff Barnett wrote:

    However if your point is that you can build "simulations" of one sort of scoping out of primitives for another sort ...

    No, my point was that there seems to be no need for dynamic scoping, if
    the only excuse anyone can come up for needing it comes out of exception handling.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Didier Verna@21:1/5 to Paul Rubin on Sun Mar 17 10:41:01 2024
    Paul Rubin <no.email@nospam.invalid> wrote:

    Does anyone know when lexical scope started appearing in Lisp? Not
    counting Scheme, did it exist in predecessors of Common Lisp? Was it
    used much? Is it really true that Common Lisp had both lexical and
    dynamic scope in order to support older code that was written relying
    on dynamic scope and was too hard to convert?

    That question should be answered by someone from the standardizing
    committee, but anyway I think that there's two questions here.

    Dynamic scope is necessary for some things (e.g. mutable global
    variables, the condition system, thread-local storage, etc.), and then
    there is the general user-level facility for declaring special
    variables. So it might very well be that the original motivations for
    those are different...

    --
    Resistance is futile. You will be jazzimilated.

    Jazz site: http://www.didierverna.com
    Other sites: http://www.didierverna.info

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Didier Verna on Sun Mar 17 21:12:05 2024
    On Sun, 17 Mar 2024 10:41:01 +0100, Didier Verna wrote:

    Dynamic scope is necessary for some things (e.g. mutable global
    variables, the condition system, thread-local storage, etc.) ...

    Can’t see the reasoning behind any of those three examples. Thread-local storage is the same as global variables, and global scope is just the
    outermost lexical scope. As for the condition system, see my example
    showing how a lexically-scoped language like Python can handle that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Jeff Barnett on Sun Mar 17 23:05:22 2024
    On Sun, 17 Mar 2024 16:42:34 -0600, Jeff Barnett wrote:

    Just think of the following when specifying a variable's intent: is it supposed to influence an evaluation or is it supposed to influence an evaluation when it's in a particular lexical scope? Does that help you
    any?

    Seems you phrased that wrong: it should be “is it supposed to influence an evaluation or is it supposed to influence an evaluation when it’s in a particular *dynamic* scope”? Because a variable in a lexical scope is
    always in that lexical scope, no “when” about it.

    The example of error handling is that it's generally supposed to control
    an evaluation. If error handling was lexically based ...

    I didn’t say error handling was lexically based, I said the matching of exceptions was lexically based. I thought my example made that distinction clear.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jeff Barnett@21:1/5 to Lawrence D'Oliveiro on Sun Mar 17 16:42:34 2024
    On 3/16/2024 11:37 PM, Lawrence D'Oliveiro wrote:
    On Sat, 16 Mar 2024 23:13:20 -0600, Jeff Barnett wrote:

    However if your point is that you can build "simulations" of one sort of
    scoping out of primitives for another sort ...

    No, my point was that there seems to be no need for dynamic scoping, if
    the only excuse anyone can come up for needing it comes out of exception handling.

    That wasn't the only thing mentioned. In fact it was said to be quite
    useful and desired without enumeration of reasons. You of course chopped
    many original messages above to remove some of those "short glowing
    reviews" then got on your hobby horse and started to ride.

    Just think of the following when specifying a variable's intent: is it
    supposed to influence an evaluation or is it supposed to influence an evaluation when it's in a particular lexical scope? Does that help you any?

    The example of error handling is that it's generally supposed to control
    an evaluation. If error handling was lexically based then you would need
    to decide what to do when evaluate(x) occurs and the handling of certain conditions are specified in a scope not available when x was defined.
    There are choices and lots of them. When dynamic and lexical flow trees
    are different, you must decide by language rules or provide primitives
    to users that allows them to sort out intent. If you don't have any
    "special" variables around, you'll find that rather difficult.

    Lexical-only is nice and neat for many applications and philosophies of programming. But not all by a long shot. If that approach satisfies you,
    I'd suggest ALGOL or even SIMULA. Alan Perlis even saw uses for dynamic visibility (private communications) and discussed the thought that OWN variables did not satisfy those uses. There was an ambiguity in the
    second ALGOL spec on the requirements for OWN variables: if the spec was
    read one way, a particular function computed a Legendre polynomial; read
    the other way, it computed a Bessel function! (It's been a long time and
    I don't remember the exact pair of function families that were
    confounded.) In any event, the issue had to do for the rules about when
    an OWN needed to be bound and it was possible to have multiple bindings simultaneously! I believe this latter fact was tied up with trying to
    allow OWNS to act more like SPECIAL.
    --
    Jeff Barnett

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to jbb@notatt.com on Mon Mar 18 14:47:29 2024
    In article <ut7rkt$3p750$1@dont-email.me>,
    Jeff Barnett <jbb@notatt.com> wrote:
    second ALGOL spec on the requirements for OWN variables: if the spec was

    You can't refer to ALGOL like this. It is either ALGOL60 or ALGOL68.
    These are totally different languages.
    ALGOL60 is an early experiment. ALGOL68 is practically Google's go.

    --
    Jeff Barnett

    --
    Don't praise the day before the evening. One swallow doesn't make spring.
    You must not say "hey" before you have crossed the bridge. Don't sell the
    hide of the bear until you shot it. Better one bird in the hand than ten in
    the air. First gain is a cat purring. - the Wise from Antrim -

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to albert on Mon Mar 18 19:51:27 2024
    On Mon, 18 Mar 2024 14:47:29 +0100, albert wrote:

    ALGOL60 is an early experiment.

    Actually, the “early experiment” was IAL, a.k.a. ALGOL58.

    And then there was “ALGOL-W”, a.k.a. “Wirth-Hoare ALGOL”, which was the precursor to Pascal.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From George Neuner@21:1/5 to albert@spenarnc.xs4all.nl on Mon Mar 18 20:33:08 2024
    On Mon, 18 Mar 2024 14:47:29 +0100, albert@spenarnc.xs4all.nl wrote:

    In article <ut7rkt$3p750$1@dont-email.me>,
    Jeff Barnett <jbb@notatt.com> wrote:
    second ALGOL spec on the requirements for OWN variables: if the spec was

    You can't refer to ALGOL like this. It is either ALGOL60 or ALGOL68.
    These are totally different languages.
    ALGOL60 is an early experiment. ALGOL68 is practically Google's go.


    FWIW: ALGOL68 was described by two different reports using completely
    different grammar notation.

    [1] Report on the Algorithmic Language ALGOL 68
    [2] Revised Report on the Algorithmic Language ALGOL 68

    The original report used a "Van Wijngaarden" (vW) grammar. vW grammars
    are 2-level, consisting of a conventional attribute grammar, augmented
    by a "meta" grammar which describes the possible values of attributes
    in the 1st grammar. In theory, a vW grammar can do wonderful things
    such as turn type errors into syntax errors.

    Unfortunately, vW grammars are Turing Complete - programming languages
    in themselves. The grammar in the ALGOL 68 report widely was
    considered to be too difficult for many compiler implementors to
    understand.

    Thus the report was rewritten using an adhoc notation called "NEST"
    [which AFAICT was never used again]. NEST also was 2-level and able to
    describe attribute values, but more verbose than vW, and deliberately
    made not Turing Complete.


    However, the grammars in the two reports are so different that they
    are difficult to compare, and there was/is some debate as to whether
    they truly describe the same language. In any event, almost all
    AlGOL68 compilers were said to have been developed using the revised
    report.

    [1] https://pdfroom.com/books/report-on-the-algorithmic-language-algol-68/Jr2ELK1agyv
    [2] https://www.softwarepreservation.org/projects/ALGOL/report/Algol68_revised_report-AB-600dpi.pdf

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jeff Barnett@21:1/5 to All on Mon Mar 18 23:02:23 2024
    T24gMy8xNy8yMDI0IDU6MDUgUE0sIExhd3JlbmNlIEQnT2xpdmVpcm8gd3JvdGU6DQo+IE9u IFN1biwgMTcgTWFyIDIwMjQgMTY6NDI6MzQgLTA2MDAsIEplZmYgQmFybmV0dCB3cm90ZToN Cj4gDQo+PiBKdXN0IHRoaW5rIG9mIHRoZSBmb2xsb3dpbmcgd2hlbiBzcGVjaWZ5aW5nIGEg dmFyaWFibGUncyBpbnRlbnQ6IGlzIGl0DQo+PiBzdXBwb3NlZCB0byBpbmZsdWVuY2UgYW4g ZXZhbHVhdGlvbiBvciBpcyBpdCBzdXBwb3NlZCB0byBpbmZsdWVuY2UgYW4NCj4+IGV2YWx1 YXRpb24gd2hlbiBpdCdzIGluIGEgcGFydGljdWxhciBsZXhpY2FsIHNjb3BlPyBEb2VzIHRo YXQgaGVscCB5b3UNCj4+IGFueT8NCj4gDQo+IFNlZW1zIHlvdSBwaHJhc2VkIHRoYXQgd3Jv bmc6IGl0IHNob3VsZCBiZSDigJxpcyBpdCBzdXBwb3NlZCB0byBpbmZsdWVuY2UgYW4NCj4g ZXZhbHVhdGlvbiBvciBpcyBpdCBzdXBwb3NlZCB0byBpbmZsdWVuY2UgYW4gZXZhbHVhdGlv biB3aGVuIGl04oCZcyBpbiBhDQo+IHBhcnRpY3VsYXIgKmR5bmFtaWMqIHNjb3Bl4oCdPyBC ZWNhdXNlIGEgdmFyaWFibGUgaW4gYSBsZXhpY2FsIHNjb3BlIGlzDQo+IGFsd2F5cyBpbiB0 aGF0IGxleGljYWwgc2NvcGUsIG5vIOKAnHdoZW7igJ0gYWJvdXQgaXQuDQoNCkdpdmVuIHRo ZSBzb3J0IG9mIGxhbmd1YWdlcyB3ZSBhcmUgdGFsa2luZyBhYm91dCAoZS5nLiwgbm90IENS SVNQIA0KZGVzY3JpYmVkIGFib3ZlKSB5b3UgcmVhbGx5IGNhbid0IGVzY2FwZSBmcm9tIG9u ZSBkeW5hbWljIHNjb3BlIHRvIA0KYW5vdGhlci4gWW91IG5lZWQgY29yb3V0aW5lcyB0aGF0 IGhhdmUgbm8gc3RhcnQvcmVzdGFydCBjb21taXRtZW50cyB0byANCm9uZSBhbm90aGVyLg0K DQo+PiBUaGUgZXhhbXBsZSBvZiBlcnJvciBoYW5kbGluZyBpcyB0aGF0IGl0J3MgZ2VuZXJh bGx5IHN1cHBvc2VkIHRvIGNvbnRyb2wNCj4+IGFuIGV2YWx1YXRpb24uIElmIGVycm9yIGhh bmRsaW5nIHdhcyBsZXhpY2FsbHkgYmFzZWQgLi4uDQo+IA0KPiBJIGRpZG7igJl0IHNheSBl cnJvciBoYW5kbGluZyB3YXMgbGV4aWNhbGx5IGJhc2VkLCBJIHNhaWQgdGhlIG1hdGNoaW5n IG9mDQo+IGV4Y2VwdGlvbnMgd2FzIGxleGljYWxseSBiYXNlZC4gSSB0aG91Z2h0IG15IGV4 YW1wbGUgbWFkZSB0aGF0IGRpc3RpbmN0aW9uDQo+IGNsZWFyLg0KDQpNYXliZSBpdCBkaWQg YnV0IG5vdCB0byBtZS4NCi0tIA0KSmVmZiBCYXJuZXR0DQoNCg==

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Jeff Barnett on Tue Mar 19 05:44:55 2024
    On Mon, 18 Mar 2024 23:02:23 -0600, Jeff Barnett wrote:

    ... you really can't escape from one dynamic scope to
    another.

    Au contraire, with dynamic scoping, such “escaping” happens all the time.

    Here’s an example from a language, namely Perl, which does allow for
    dynamic scoping (in fact, early on it had no lexical scoping):

    $a = 1;

    sub f1()
    {
    $a = $a + 1;
    } # f1

    sub f2()
    {
    local $a = 3;
    print "inner ", $a, "\n";
    f1();
    print "inner ", $a, "\n";
    } # f2

    f1();
    print "outer ", $a, "\n";
    f2();
    print "outer ", $a, "\n";
    f1();
    print "outer ", $a, "\n";

    The output is

    outer 2
    inner 3
    inner 4
    outer 2
    outer 3

    I didn’t say error handling was lexically based, I said the matching of
    exceptions was lexically based. I thought my example made that
    distinction clear.

    Maybe it did but not to me.

    Look back again, and see how the outer exception is not the same as the
    one with the same name local to the function, yet the catch clauses search
    for the exceptions according to dynamic execution nesting.

    Want me to go over it step-by-step?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Johanne Fairchild@21:1/5 to Paul Rubin on Tue Mar 19 06:07:08 2024
    Paul Rubin <no.email@nospam.invalid> writes:

    Does anyone know when lexical scope started appearing in Lisp?

    You should find some information in

    Steele, Guy L., and Richard P. Gabriel. "The evolution of Lisp."
    History of programming languages---II. 1996. 233-330.
    https://dl.acm.org/doi/pdf/10.1145/234286.1057818

    For instance:

    --8<---------------cut here---------------start------------->8---
    When the IBM 370 came out, Lisp370 implementation began. [...]

    Lisp370 supported both special binding and lexical binding, as well as
    closures over both lexical and special variables, using a technique
    similar to spaghetti stacks. (Page 31.)
    --8<---------------cut here---------------end--------------->8---

    --8<---------------cut here---------------start------------->8---
    Scheme was one of the first languages to have taken seriously the
    implications of lexical scoping (as opposed to local scoping, which
    had been in use in Lisp compilers for almost a decade) and first-class
    functions. (Page 23.)
    --8<---------------cut here---------------end--------------->8---

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)