Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.lisp > #59450 > unrolled thread

History of lexical scope in Lisp

Started byPaul Rubin <no.email@nospam.invalid>
First post2024-03-15 11:55 -0700
Last post2024-03-19 06:07 -0300
Articles 20 on this page of 24 — 9 participants

Back to article view | Back to comp.lang.lisp


Contents

  History of lexical scope in Lisp Paul Rubin <no.email@nospam.invalid> - 2024-03-15 11:55 -0700
    Re: History of lexical scope in Lisp Jeff Barnett <jbb@notatt.com> - 2024-03-15 16:05 -0600
      Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-15 22:26 +0000
        Re: History of lexical scope in Lisp Kaz Kylheku <433-929-6894@kylheku.com> - 2024-03-15 23:04 +0000
        Re: History of lexical scope in Lisp Jeff Barnett <jbb@notatt.com> - 2024-03-16 00:46 -0600
          Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-16 07:27 +0000
            Re: History of lexical scope in Lisp Jeff Barnett <jbb@notatt.com> - 2024-03-16 23:13 -0600
              Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-17 05:37 +0000
                Re: History of lexical scope in Lisp Jeff Barnett <jbb@notatt.com> - 2024-03-17 16:42 -0600
                  Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-17 23:05 +0000
                    Re: History of lexical scope in Lisp Jeff Barnett <jbb@notatt.com> - 2024-03-18 23:02 -0600
                      Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-19 05:44 +0000
                  Re: History of lexical scope in Lisp albert@spenarnc.xs4all.nl - 2024-03-18 14:47 +0100
                    Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-18 19:51 +0000
                    Re: History of lexical scope in Lisp George Neuner <gneuner2@comcast.net> - 2024-03-18 20:33 -0400
          Re: History of lexical scope in Lisp Kaz Kylheku <433-929-6894@kylheku.com> - 2024-03-16 16:54 +0000
    Re: History of lexical scope in Lisp Robert Brown <robert.brown@gmail.com> - 2024-03-15 20:23 -0400
      Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-16 01:03 +0000
        Re: History of lexical scope in Lisp Robert Brown <robert.brown@gmail.com> - 2024-03-16 11:32 -0400
          Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-16 21:59 +0000
    Re: History of lexical scope in Lisp Kaz Kylheku <433-929-6894@kylheku.com> - 2024-03-16 02:37 +0000
    Re: History of lexical scope in Lisp  Didier Verna <didier@didierverna.net> - 2024-03-17 10:41 +0100
      Re: History of lexical scope in Lisp Lawrence D'Oliveiro <ldo@nz.invalid> - 2024-03-17 21:12 +0000
    Re: History of lexical scope in Lisp Johanne Fairchild <jfairchild@tudado.org> - 2024-03-19 06:07 -0300

Page 1 of 2  [1] 2  Next page →


#59450 — History of lexical scope in Lisp

FromPaul Rubin <no.email@nospam.invalid>
Date2024-03-15 11:55 -0700
SubjectHistory of lexical scope in Lisp
Message-ID<874jd7z5nf.fsf@nightsong.com>
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.

[toc] | [next] | [standalone]


#59451

FromJeff Barnett <jbb@notatt.com>
Date2024-03-15 16:05 -0600
Message-ID<ut2gnj$2g8k5$1@dont-email.me>
In reply to#59450
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

[toc] | [prev] | [next] | [standalone]


#59452

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-15 22:26 +0000
Message-ID<ut2hvh$2gft8$1@dont-email.me>
In reply to#59451
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.

[toc] | [prev] | [next] | [standalone]


#59453

FromKaz Kylheku <433-929-6894@kylheku.com>
Date2024-03-15 23:04 +0000
Message-ID<20240315155740.155@kylheku.com>
In reply to#59452
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

[toc] | [prev] | [next] | [standalone]


#59457

FromJeff Barnett <jbb@notatt.com>
Date2024-03-16 00:46 -0600
Message-ID<ut3f7t$2p3sa$1@dont-email.me>
In reply to#59452
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.
-- 
Jeff Barnett

[toc] | [prev] | [next] | [standalone]


#59458

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-16 07:27 +0000
Message-ID<ut3hlt$2pi20$1@dont-email.me>
In reply to#59457
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.

[toc] | [prev] | [next] | [standalone]


#59463

FromJeff Barnett <jbb@notatt.com>
Date2024-03-16 23:13 -0600
Message-ID<ut5u5i$3c0e6$1@dont-email.me>
In reply to#59458
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

[toc] | [prev] | [next] | [standalone]


#59464

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-17 05:37 +0000
Message-ID<ut5vjg$3c7pv$2@dont-email.me>
In reply to#59463
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.

[toc] | [prev] | [next] | [standalone]


#59467

FromJeff Barnett <jbb@notatt.com>
Date2024-03-17 16:42 -0600
Message-ID<ut7rkt$3p750$1@dont-email.me>
In reply to#59464
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

[toc] | [prev] | [next] | [standalone]


#59468

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-17 23:05 +0000
Message-ID<ut7svi$3pbnh$2@dont-email.me>
In reply to#59467
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.

[toc] | [prev] | [next] | [standalone]


#59472

FromJeff Barnett <jbb@notatt.com>
Date2024-03-18 23:02 -0600
Message-ID<utb691$leks$1@dont-email.me>
In reply to#59468
On 3/17/2024 5:05 PM, Lawrence D'Oliveiro wrote:
> 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.

Given the sort of languages we are talking about (e.g., not CRISP 
described above) you really can't escape from one dynamic scope to 
another. You need coroutines that have no start/restart commitments to 
one another.

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

Maybe it did but not to me.
-- 
Jeff Barnett

[toc] | [prev] | [next] | [standalone]


#59473

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-19 05:44 +0000
Message-ID<utb8on$lttb$1@dont-email.me>
In reply to#59472
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?

[toc] | [prev] | [next] | [standalone]


#59469

Fromalbert@spenarnc.xs4all.nl
Date2024-03-18 14:47 +0100
Message-ID<nnd$79e8f550$72629b38@7ae82f9f1ca9b0e9>
In reply to#59467
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 -

[toc] | [prev] | [next] | [standalone]


#59470

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-18 19:51 +0000
Message-ID<uta5vv$bdg5$3@dont-email.me>
In reply to#59469
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.

[toc] | [prev] | [next] | [standalone]


#59471

FromGeorge Neuner <gneuner2@comcast.net>
Date2024-03-18 20:33 -0400
Message-ID<bgehvi9elnkgo6qjchi1dvlks1du5tvo0s@4ax.com>
In reply to#59469
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

[toc] | [prev] | [next] | [standalone]


#59461

FromKaz Kylheku <433-929-6894@kylheku.com>
Date2024-03-16 16:54 +0000
Message-ID<20240316093534.951@kylheku.com>
In reply to#59457
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/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @Kazinator@mstdn.ca

[toc] | [prev] | [next] | [standalone]


#59454

FromRobert Brown <robert.brown@gmail.com>
Date2024-03-15 20:23 -0400
Message-ID<871q8bja7c.fsf@gmail.com>
In reply to#59450
Paul Rubin <no.email@nospam.invalid> writes:

> 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?

No.  I'd say Common Lisp has both lexical and dynamic scope because both
are very useful.  For instance, the Guice dependency injection framework
for Java implements @RequestScoped settings, which are dynamically
scoped and thread local.  Guice wouldn't need that feature if Java
natively supported dynamically-scoped variables.

[toc] | [prev] | [next] | [standalone]


#59455

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-16 01:03 +0000
Message-ID<ut2r5n$2i048$4@dont-email.me>
In reply to#59454
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...

[toc] | [prev] | [next] | [standalone]


#59460

FromRobert Brown <robert.brown@gmail.com>
Date2024-03-16 11:32 -0400
Message-ID<87il1mgpk1.fsf@gmail.com>
In reply to#59455
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

[toc] | [prev] | [next] | [standalone]


#59462

FromLawrence D'Oliveiro <ldo@nz.invalid>
Date2024-03-16 21:59 +0000
Message-ID<ut54ne$33m2r$5@dont-email.me>
In reply to#59460
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”?

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.lisp


csiph-web