• Closures

    From none) (albert@21:1/5 to All on Mon Aug 14 10:19:55 2023
    XPost: comp.lang.lisp

    I finally understood what closures mean.
    In Forth parlance it is a search order that is kept with an
    Forth word. Each time the Forth word is invoked, the search
    order is obeyed on top of the parameters that are passed.
    It puzzled me that the search order need not be restored after
    calling a lisp word, but it is build in.
    Restoring seemed to be a stumbling block for implementing
    tail optimisation, but it is not needed.

    In implementing lisp the lisp search order has to be separate
    from the Forth search order, that is naturally be present,
    because Forth is the implementation language.
    Traditional Forth implementation have a voc link, that points
    to a previous wordlist, and allows Forth to print a list of
    VOCABULARY's. It can be put to good use to implement the
    chain of nested environments that lisp requires.

    An environment is a wordlist, and a Forth mechanism can be used
    to store lisp function in the wordlist.
    That ties up the name field (to identify) and the link field (to
    search).
    Because it is a function defined in a high level Forth,
    the code field contain docol, and the data field contains
    a pointer to high level code.
    The flag field is put to good use in identifying lisp
    objects like numbers string symbols.

    So the CDFLN fields are tied up, lisp functions run out of Forth header
    fields.
    Comes ciforth. The implementor of ciforth had the foresight to define
    an extra field (>XFA) , free to use.

    The bottom line is you can have a lisp closure call as follows:
    \ Execute a lisp execution-token .
    : EXECUTE-LISP DUP >XFA @ ENVIR ! EXECUTE ;

    Groetjes Albert
    --
    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 spinning. - the Wise from Antrim -

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to none albert on Mon Aug 14 05:07:08 2023
    none albert schrieb am Montag, 14. August 2023 um 10:19:59 UTC+2:
    I finally understood what closures mean.
    In Forth parlance it is a search order that is kept with an
    Forth word. Each time the Forth word is invoked, the search
    order is obeyed on top of the parameters that are passed.

    The lexical environment of a closure comprises "only" the captured
    variables (upvalues). Storing the complete search order seems like overkill.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to albert@cherry. on Mon Aug 14 14:17:56 2023
    XPost: comp.lang.lisp

    albert@cherry.(none) (albert) writes:
    I finally understood what closures mean.
    In Forth parlance it is a search order that is kept with an
    Forth word.

    That view leads down to boy compilers (in Knuth's man-or-boy test),
    and at best (if you save and restore the variables on function entry
    and exit) dynamically-scoped Lisp.

    An environment is a wordlist,

    A wordlist has only one instance of each word in a wordlist.

    An environment in a statically-scoped language is a set of local
    frames, where each local frame is created dynamically when the
    function to which the frame belongs is called. So if a function has
    two instances at the same time (e.g., in recursion), a wordlist is insufficient.

    You can use wordlists to store the offsets of variable within the
    frames, but you have to manage the frames separately.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to albert@cherry on Mon Aug 14 15:27:25 2023
    XPost: comp.lang.lisp

    On 2023-08-14, albert@cherry.(none) (albert) <albert@cherry> wrote:
    I finally understood what closures mean.
    In Forth parlance it is a search order that is kept with an
    Forth word.

    It seems as if you emigrated to Forth Island decades ago and now you
    need everything translated into Forthese.

    --
    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 dxforth@21:1/5 to Kaz Kylheku on Tue Aug 15 08:10:41 2023
    XPost: comp.lang.lisp

    On 15/08/2023 1:27 am, Kaz Kylheku wrote:
    On 2023-08-14, albert@cherry.(none) (albert) <albert@cherry> wrote:
    I finally understood what closures mean.
    In Forth parlance it is a search order that is kept with an
    Forth word.

    It seems as if you emigrated to Forth Island decades ago and now you
    need everything translated into Forthese.

    Some concepts that abound are baffling and need translating.
    OTOH forth afficionados come up with novelties too :)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to dxforth on Tue Aug 15 00:20:44 2023
    XPost: comp.lang.lisp

    On 2023-08-14, dxforth <dxforth@gmail.com> wrote:
    On 15/08/2023 1:27 am, Kaz Kylheku wrote:
    On 2023-08-14, albert@cherry.(none) (albert) <albert@cherry> wrote:
    I finally understood what closures mean.
    In Forth parlance it is a search order that is kept with an
    Forth word.

    It seems as if you emigrated to Forth Island decades ago and now you
    need everything translated into Forthese.

    Some concepts that abound are baffling and need translating.
    OTOH forth afficionados come up with novelties too :)

    Large numbers of more or less empty heads that don't know anything other
    than Javascript are able to grok closures.

    Relating things to what you know is a useful crutch, but at
    some point you have to just walk.

    --
    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 none) (albert@21:1/5 to Anton Ertl on Tue Aug 15 08:44:44 2023
    XPost: comp.lang.lisp

    In article <2023Aug14.161756@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    albert@cherry.(none) (albert) writes:
    I finally understood what closures mean.
    In Forth parlance it is a search order that is kept with an
    Forth word.

    That view leads down to boy compilers (in Knuth's man-or-boy test),
    and at best (if you save and restore the variables on function entry
    and exit) dynamically-scoped Lisp.
    I'm an amateur. I implement MAL. I don't know whether that is a dynamically-scoped Lisp. Actually my goal is to prove that
    Forth is capable of parsing lisp itself, with a modular parser
    in the style of BEGIN WHILE REPEAT words.
    For example the rule that comma is to be treated as blank space
    translate to the rule:
    : , ; PREFIX


    An environment is a wordlist,

    A wordlist has only one instance of each word in a wordlist.
    Okay, so we have multiple instance of a wordlist.


    An environment in a statically-scoped language is a set of local
    frames, where each local frame is created dynamically when the
    function to which the frame belongs is called. So if a function has
    two instances at the same time (e.g., in recursion), a wordlist is >insufficient.
    That would mean that
    (def! fib (fn* (N) (if (= N 0) 1
    (if (= N 1) 1 (+ (fib (- N 1))
    (fib (- N 2)))))))
    would fail, but it isn't:
    (fib 10)
    89

    Also the quine tests succeeds. It was commented out of the tests,
    because the line was too long. (and it is too long for a usenet post,
    sorry). That was not a restriction in my implementation.

    ;;;;; Test quine
    ((fn* [q] (quasiquote ((unquote q) (quote (unquote q))))) (quote (fn* [q] (quasiquote ((unquote q) (quote (unquote q)))))))
    ((fn* [q] (quasiquote ((unquote q) (quote (unquote q))))) (quote (fn* [q] (quasiquote ((unquote q) (quote (unquote q)))))))

    What fails is:
    ( ( (fn* (a) (fn* (b) (+ a b))) 5) 7)
    (b) (+ a b))) 5) 7) ? ciforth ERROR # 8010
    Error 8010 means that a symbol is not found in the nested environments.
    `` a '' is not found.
    The phrase clearly means that in the environment that `a has the
    value 7, a new instance of the function has to be generated.

    You can use wordlists to store the offsets of variable within the
    frames, but you have to manage the frames separately.
    I think I do with the proposed mechanism.
    We shall see.


    - anton
    --
    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 spinning. - the Wise from Antrim -

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to albert@cherry on Tue Aug 15 07:33:15 2023
    XPost: comp.lang.lisp

    On 2023-08-15, albert@cherry.(none) (albert) <albert@cherry> wrote:
    That would mean that
    (def! fib (fn* (N) (if (= N 0) 1
    (if (= N 1) 1 (+ (fib (- N 1))
    (fib (- N 2)))))))
    would fail, but it isn't:
    (fib 10)

    fib is not capturing lexical closures. There are multiple instances, but
    they are not accessible at the same time. In particular, once any
    activation of fib terminates, nothing accesses that instance of N any
    more.

    We can write fib in C, whose local variables turn to pixie dust when the
    block scope ends, allowing a simple stack to be used for locals.

    Lexical closures mean that the variables visible in any activation of a function can live indefinitely. Lexical closures are objects that can
    escape from the context where they are created, and be invoked after
    that context has terminated.

    --
    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 none) (albert@21:1/5 to 864-117-4973@kylheku.com on Tue Aug 15 10:40:54 2023
    XPost: comp.lang.lisp

    In article <20230815002701.354@kylheku.com>,
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    On 2023-08-15, albert@cherry.(none) (albert) <albert@cherry> wrote:
    That would mean that
    (def! fib (fn* (N) (if (= N 0) 1
    (if (= N 1) 1 (+ (fib (- N 1))
    (fib (- N 2)))))))
    would fail, but it isn't:
    (fib 10)

    fib is not capturing lexical closures. There are multiple instances, but
    they are not accessible at the same time. In particular, once any
    activation of fib terminates, nothing accesses that instance of N any
    more.

    Obviously not, I observed that. The test for capturing lexical closures
    fails, but fib is not.

    <SNIP>
    Lexical closures mean that the variables visible in any activation of a >function can live indefinitely. Lexical closures are objects that can
    escape from the context where they are created, and be invoked after
    that context has terminated.
    This is lisp parlance. The concept of lexical closures is present
    in algol 68, but the term is not used.
    Any language that supports functions within functions, functions as
    first order objects and persistance (items created in a function that
    have a life span longer than the function call) has this feature.

    Mastodon: @Kazinator@mstdn.ca

    Groetjes Albert
    --
    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 spinning. - the Wise from Antrim -

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Kaz Kylheku on Tue Aug 15 06:02:54 2023
    Kaz Kylheku schrieb am Dienstag, 15. August 2023 um 09:33:18 UTC+2:
    On 2023-08-15, albert@cherry.(none) (albert) <albert@cherry> wrote:
    That would mean that
    (def! fib (fn* (N) (if (= N 0) 1
    (if (= N 1) 1 (+ (fib (- N 1))
    (fib (- N 2)))))))
    would fail, but it isn't:
    (fib 10)
    fib is not capturing lexical closures. There are multiple instances, but
    they are not accessible at the same time. In particular, once any
    activation of fib terminates, nothing accesses that instance of N any
    more.

    We can write fib in C, whose local variables turn to pixie dust when the block scope ends, allowing a simple stack to be used for locals.

    Lexical closures mean that the variables visible in any activation of a function can live indefinitely. Lexical closures are objects that can
    escape from the context where they are created, and be invoked after
    that context has terminated.

    Yes. I also found this nice short discussion: https://stackoverflow.com/questions/37366822/when-to-free-a-closures-memory-in-a-lisp-interpreter

    Another aspect is that since Forth has no garbage collection, naive closures could lead to memory leaks. Particularly nasty in resource constrained systems.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Sat Mar 9 19:20:11 2024
    Anton Ertl wrote:

    minforth@gmx.net (minforth) writes:
    My quotation "model" with access to upvalues works here.
    Useful now and then, but they cannot pass the man-or-boy test.

    Why not?

    It falls into the funarg problem category because I use a locals
    stack. Reading/writing locals of the enclosing function from within
    an enclosed quotation is as easy as reaching deeper down the stack.

    So there is no recursion-proof activation record per xt as was
    needed for proper closures.

    What use did you find for them?

    Calling embedded functions (quotations) without having to pass parameters
    or having to use glocbal variables can result in concise readable notation.

    Here it is sometimes handy to "not" pass longish matrices around, just fetch them from the caller's array locals one level up.

    OTOH I have absolutely no uses case for closures. Also my gut feeling
    tells me they don't fulfill the KISS principle.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to albert@spenarnc.xs4all.nl on Sat Mar 9 20:06:15 2024
    albert@spenarnc.xs4all.nl writes:
    I always thought that closures are invented by lispers, because
    in lisp you cannot write normal programs.

    Lisp didn't get them until fairly late in its evolution, I think. Maybe
    old time Lispers here would know. But I think Scheme was the first
    dialect that really made use of them. Algol 60 its own version much
    earlier, in the form of call-by-name parameters.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to albert@spenarnc.xs4all.nl on Sun Mar 10 08:12:10 2024
    albert@spenarnc.xs4all.nl wrote:
    I find this extremely interesting. The Pascal specification is
    clear. I'd not thought that - for me - an obscure feature as
    closures is present in Pascal, much less that you could remove
    this feature.

    IIRC Pascal had nested functions from the beginning, unlike C
    where they still don't exist. Closures are especially interesting
    for functional programming. Since Forth is typeless and treats
    execution tokens, addresses and numbers the same, functional
    programming is perhaps not as interesting as in other languages.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Sun Mar 10 08:40:57 2024
    Paul Rubin <no.email@nospam.invalid> writes:
    albert@spenarnc.xs4all.nl writes:
    I always thought that closures are invented by lispers, because
    in lisp you cannot write normal programs.

    Lisp didn't get them until fairly late in its evolution, I think. Maybe
    old time Lispers here would know. But I think Scheme was the first
    dialect that really made use of them.

    Yes, Scheme was the first implemented Lisp with lexical scoping and it
    allows treating closures as first-class values. However, static
    scoping was the original intention of McCarthy when he designed Lisp,
    and he considered the dynamically scoped implementation to be a bug.
    However, Hyrum's law struck, and mainstream Lisp kept dynamic scoping,
    with static scoping being added in Common Lisp.

    Algol 60 its own version much
    earlier, in the form of call-by-name parameters.

    That, too, but Algol 60 also has nested functions/procedures with
    static scoping. Algol 60 does not have first-class closures, though,
    and some people reservere the name "closure" to first-class closures.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to minforth on Mon Mar 11 10:44:28 2024
    In article <f5f532f1cb4d8acb527965a2458510a3@www.novabbs.com>,
    minforth <minforth@gmx.net> wrote:
    albert@spenarnc.xs4all.nl wrote:
    I find this extremely interesting. The Pascal specification is
    clear. I'd not thought that - for me - an obscure feature as
    closures is present in Pascal, much less that you could remove
    this feature.

    IIRC Pascal had nested functions from the beginning, unlike C
    where they still don't exist. Closures are especially interesting
    for functional programming. Since Forth is typeless and treats
    execution tokens, addresses and numbers the same, functional
    programming is perhaps not as interesting as in other languages.

    I find nested functions, and the ability to access the variables
    of the encompassing function quite palatable. Has this something
    to do with closures?
    Anton Ertl talks about local variables from an expression that yields
    an xt. Is that the same thing?

    Contrasting Pascal with FORTRAN the main difference is namespaces,
    i.e. "vocabularies". I feel that leaving out discussing namespaces,
    in this thread is missing.

    Groetjes Albert
    --
    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 albert@spenarnc.xs4all.nl@21:1/5 to no.email@nospam.invalid on Mon Mar 11 10:34:19 2024
    In article <87frwy3f48.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:
    albert@spenarnc.xs4all.nl writes:
    I always thought that closures are invented by lispers, because
    in lisp you cannot write normal programs.

    Lisp didn't get them until fairly late in its evolution, I think. Maybe
    old time Lispers here would know. But I think Scheme was the first
    dialect that really made use of them. Algol 60 its own version much
    earlier, in the form of call-by-name parameters.

    Jenkin's device, call-by-name parameters. That was an obscure feature
    got in algol60 their by accident.
    That was a form of closure? Never had thought that.
    Algol68 replaced it by allowing reference variables, kind of type clean pointers, and passing references as parameters. That was understandable.

    Groetjes Albert
    --
    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 Paul Rubin@21:1/5 to albert@spenarnc.xs4all.nl on Mon Mar 11 20:13:24 2024
    albert@spenarnc.xs4all.nl writes:
    Jenkin's device, call-by-name parameters. That was an obscure feature
    got in algol60 their by accident.

    I think you mean

    https://en.wikipedia.org/wiki/Jensen's_device

    which I was unfamiliar with. But I thought call-by-name was not at all accidental.

    That was a form of closure?

    I would say that it is, but in Algol-60 I guess it can be stack
    allocated, unlike Scheme closures which have to be on the heap. You
    could imagine an Algol-60 extension that allowed passing procedures as
    values. That in turn might require heap allocating closures.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Tue Mar 12 07:29:16 2024
    Paul Rubin <no.email@nospam.invalid> writes:
    But I thought call-by-name was not at all
    accidental.

    What I read about Algol 60 (and I read several articles, so I cannot
    point to where I read the following; the first thing I would look at
    is the HOPL paper) says that they just wanted an elegant specification
    that (I think) supports in-out semantics. What they wrote down was call-by-name, but they were not aware of all the consequences when
    they wrote it. This only was recognized later, and was finally widely publicized with Jensen's device.

    The claim that it was unintended is supported by the fact that neither
    Algol W nor Algol 68 (two direct successors of Algol 60) have call by
    name.

    [Is a call-by-name parameter a closure]
    I would say that it is,

    It certainly accesses variables visible in the caller, like more
    typical closures. If people want to emulate closures, they usually
    pass something like an xt and execute it in the callee, just like more
    typical closures.

    but in Algol-60 I guess it can be stack
    allocated, unlike Scheme closures which have to be on the heap.

    Sophisticated Scheme compilers can determine when they can reside on
    the stack.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Tue Mar 12 11:25:33 2024
    minforth@gmx.net (minforth) writes:
    Since Forth is typeless

    That's not the case. See <https://forth-standard.org/standard/usage#section.3.1>.

    and treats
    execution tokens, addresses and numbers the same, functional
    programming is perhaps not as interesting as in other languages.

    Why should that be the case?

    Lisp has also been called typeless, and it's one of the oldest
    functional programming languages.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Anton Ertl on Wed Mar 13 18:16:42 2024
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    is the HOPL paper) says that they just wanted an elegant specification
    that (I think) supports in-out semantics. What they wrote down was call-by-name, but they were not aware of all the consequences when
    they wrote it.

    I don't remember Algol syntax but I had thought using call-by-name as a
    cheap inline function was idiomatic in it. E.g. to add up the first n
    squares, you could say

    a = sum(i, 1, n, i*i)

    where sum was defined something like (pseudocode):

    but in Algol-60 I guess it can be stack allocated, unlike Scheme
    closures which have to be on the heap.

    Sophisticated Scheme compilers can determine when they can reside on
    the stack.

    Sure, but Algol-60 didn't create the possibility of having to heap
    allocate anything. So it avoided needing GC, which would have been a
    big minus in that era. Lisp existed then but idk if it was actually
    used for anything outside of research.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Thu Mar 14 09:17:39 2024
    Paul Rubin <no.email@nospam.invalid> writes:
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    is the HOPL paper) says that they just wanted an elegant specification
    that (I think) supports in-out semantics. What they wrote down was
    call-by-name, but they were not aware of all the consequences when
    they wrote it.

    I don't remember Algol syntax but I had thought using call-by-name as a
    cheap inline function was idiomatic in it. E.g. to add up the first n >squares, you could say

    a = sum(i, 1, n, i*i)

    It may have become idiomatic after Jensen's device became well-known.
    That does not mean that it was intended.

    However, I don't think that it became idiomatic, because if it had
    become idiomatic, the successor languages of Algol 60 would have
    supported call by name, maybe as default, or maybe as a special option
    for passing parameters (syntactically similar to the VAR parameters in
    Pascal). None of that happened.

    If you want to see what happens if something becomes idiomatic, look
    at Lisp: The intention for the language was lexical scoping, but the implementation used dynamic scoping. By the time this was recognized
    as a bug, enough programs had been written that relied on dynamic
    scoping and enough programmers had become accustomed to this behaviour
    that they could not just fix it, but instead used a workaround (the
    FUNARG device) when they wanted to have lexical-scoping semantics.
    Eventually Common Lisp (started 1981, released 1984) added a separate
    syntax for lexical scoping to mainstream Lisp, but that was more than
    two decades after dynamically scoped Lisp had been implemented and
    become idiomatic.

    Another case is the story of S-expressions vs. (Algol- or ML-like) M-expressions in Lisp.

    Sure, but Algol-60 didn't create the possibility of having to heap
    allocate anything. So it avoided needing GC, which would have been a
    big minus in that era. Lisp existed then but idk if it was actually
    used for anything outside of research.

    And yet, Lisp had so much existing code by the time the scoping
    implementation was discovered as being buggy that they could not fix
    it. Algol-60 has been described as a publication language, so maybe
    there was actually more running Lisp code around than Algol-60 code.
    Sure, Burroughs used Algol-60 for their large systems, but they and
    their customers did not like Jensen's device themselves, or they did
    not participate in the development of other programming languages that
    received any scrutiny in language design discussions. In any case, call-by-name does not appear in any later languages that I have ever
    heard of.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Anton Ertl on Thu Mar 14 15:57:01 2024
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    It may have become idiomatic after Jensen's device became well-known.
    That does not mean that it was intended.

    Peter Naur's retrospective on Algol-60 history <https://dl.acm.org/doi/10.1145/800025.1198353> doesn't discuss this
    aspect in detail, but (page 38 of the pdf) Fritz Bauer commented:

    A particularly sad experience is described with the events after the
    conference~ had ended. Samelson and others had unsuccessfully tried
    to have procedures as parameters included in ALGOL 60 in a
    straightforward manner (This seemingly is not recorded by Peter
    Naur, showing the arbitrariness of working along the written notes
    only). Alan Perlis then with proposal 48 of Jan. 20 admitted that he
    understood the "call by name" to be a hidden introduction of
    procedure parameters (later to be known as Jensen's trick). The
    remark "seemingly was only understood in a coherent manner by one
    member" is therefore cynical and should be omitted. It was under
    great pressure that the conference had ended and a majority had
    voted down a minority in the controversial point 45. Straightforward
    introduction of procedure parameters (and of the lambda calculus),
    as it was advocated by the minority, would have outflanked some of
    the ambiguities that made the Rome revision necessary.

    Naur's article is interesting and shows there was more dissension in the Algol-60 development process than I had realized.

    The ACM journals of the 1960s-70s and maybe later were full of
    algorithms written in Algol-60. I wonder if Jensen's device appeared
    much in them. It caught my attention that some members were apparently
    calling for explicit lambda calculus to be included, rather than
    call-by-name.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Anton Ertl on Thu Mar 14 15:19:12 2024
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    However, I don't think that it became idiomatic, because if it had
    become idiomatic, the successor languages of Algol 60 would have
    supported call by name,

    I don't see this implication. It could be idiomatic and simultaneously
    been considered a bad idea. Maybe there is an Algol textbook online
    that could say.

    The intention for [Lisp] was lexical scoping, but the implementation
    used dynamic scoping. ... Eventually Common Lisp (started 1981,
    released 1984) added a separate syntax for lexical scoping to
    mainstream Lisp, but that was more than two decades after dynamically
    scoped Lisp had been implemented and become idiomatic.

    Scheme had lexical scope in the late 1970s and I believe it appeared in
    some Lisps earlier than Common Lisp, but that was before my time. I
    might ask on the Lisp group. There is also the matter that dynamic
    scope is very easy to implement, so that might have affected what people
    did.

    Another case is the story of S-expressions vs. (Algol- or ML-like) M-expressions in Lisp.

    M-expressions never caught on because Lispers liked S-expressions.

    And yet, Lisp had so much existing code by the time the scoping implementation was discovered as being buggy that they could not fix
    it.

    I don't know about this, there are some implementation techniques
    (naming conventions) you can use to (somehwat) prevent dynamic scope
    from going awry. GNU Emacs uses that approach extensively since it
    exclusively used dynamic scope for a long time (it has lexical scope
    now).

    In any case, call-by-name does not appear in any later languages that
    I have ever heard of.

    https://www.geeksforgeeks.org/scala-functions-call-by-name/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to Anton Ertl on Fri Mar 15 14:00:44 2024
    In article <2024Mar14.101739@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    Paul Rubin <no.email@nospam.invalid> writes: >>anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    is the HOPL paper) says that they just wanted an elegant specification
    that (I think) supports in-out semantics. What they wrote down was
    call-by-name, but they were not aware of all the consequences when
    they wrote it.

    I don't remember Algol syntax but I had thought using call-by-name as a >>cheap inline function was idiomatic in it. E.g. to add up the first n >>squares, you could say

    a = sum(i, 1, n, i*i)

    It may have become idiomatic after Jensen's device became well-known.
    That does not mean that it was intended.

    However, I don't think that it became idiomatic, because if it had
    become idiomatic, the successor languages of Algol 60 would have
    supported call by name, maybe as default, or maybe as a special option
    for passing parameters (syntactically similar to the VAR parameters in >Pascal). None of that happened.

    If you want to see what happens if something becomes idiomatic, look
    at Lisp: The intention for the language was lexical scoping, but the >implementation used dynamic scoping. By the time this was recognized
    as a bug, enough programs had been written that relied on dynamic
    scoping and enough programmers had become accustomed to this behaviour
    that they could not just fix it, but instead used a workaround (the
    FUNARG device) when they wanted to have lexical-scoping semantics.
    Eventually Common Lisp (started 1981, released 1984) added a separate
    syntax for lexical scoping to mainstream Lisp, but that was more than
    two decades after dynamically scoped Lisp had been implemented and
    become idiomatic.

    Another case is the story of S-expressions vs. (Algol- or ML-like) >M-expressions in Lisp.

    Sure, but Algol-60 didn't create the possibility of having to heap
    allocate anything. So it avoided needing GC, which would have been a
    big minus in that era. Lisp existed then but idk if it was actually
    used for anything outside of research.

    And yet, Lisp had so much existing code by the time the scoping >implementation was discovered as being buggy that they could not fix
    it. Algol-60 has been described as a publication language, so maybe
    there was actually more running Lisp code around than Algol-60 code.
    Sure, Burroughs used Algol-60 for their large systems, but they and
    their customers did not like Jensen's device themselves, or they did
    not participate in the development of other programming languages that >received any scrutiny in language design discussions. In any case, >call-by-name does not appear in any later languages that I have ever
    heard of.

    In algol68 the unclean Jensen's device was replaced by references
    once it was realized what it was. This permitted the same code,
    without the mystification.



    - anton
    --
    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 Anton Ertl@21:1/5 to albert@spenarnc.xs4all.nl on Fri Mar 15 15:33:13 2024
    albert@spenarnc.xs4all.nl writes:
    In algol68 the unclean Jensen's device was replaced by references
    once it was realized what it was. This permitted the same code,
    without the mystification.

    Whatever you may mean by "unclean" and "mystification", looking at <https://rosettacode.org/wiki/Jensen%27s_Device#ALGOL_68> shows the
    header of sum to be

    PROC sum = (REF INT i, INT lo, hi, PROC REAL term)REAL:

    which is then called with

    sum (i, 1, 100, REAL: 1/i)

    So i is passed by REFerence, lo and hi are passed by value (the
    default in Algol 68, while call-by-name is the default in Algol 60),
    and 1/i is passed as PROC (the implementation mechanism behind
    call-by-name). Try using "REF" instead of "PROC", and see that the
    program does not work as intended (if it compiles at all).

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Fri Mar 15 15:53:54 2024
    Paul Rubin <no.email@nospam.invalid> writes:
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    However, I don't think that it became idiomatic, because if it had
    become idiomatic, the successor languages of Algol 60 would have
    supported call by name,

    I don't see this implication. It could be idiomatic and simultaneously
    been considered a bad idea.

    Programmers tend to love their idioms, even if others consider them to
    be a bad idea. And if programmers had loved call-by-name, we would
    have seen more programming languages that support it.

    What I see in the Algol 60, Algol W, Algol 68, and Pascal entries of <https://rosettacode.org/wiki/Jensen's_Device> is a steady progression
    away from call-by-name (and towards call-by-value):

    In Algol 60, call-by-name is the default and call-by-value has to be
    declared separately.

    In Algol W, every parameter has a declared mode: i has mode %name%, lo
    and hi have mode value, and term have mode procedure. The %name% mode
    is probably the full-blown thing which also allows assignment to it,
    while assigning to a procedure parameter is probably not allowed or
    has a different semantics.

    In Algol 68, i is passed by-REFerence, lo and hi are passed by-value
    (the default in Algol 68), and term is passed by-PROCedure (probably
    like the procedure mode of Algol 60).

    In Pascal (successor of Algol W), i is passed as a VAR parameter
    (i.e., by-reference), lo and hi are passed by-value (the default), and
    term is passed as a function parameter. The function has to be
    written separately, and in the call only its name is given. The
    rosettacode entry has a term function that gets i as a parameter, but
    I think that it is also possible to write a parameterless function
    that accesses i through lexical scoping. In either case, passing
    something in this by-name emulation in Pascal is much more cumbersome
    than in the three Algols, which shows that Wirth did not consider
    Jensen's device to be particularly important. And given the success
    of Pascal, this sentiment was shared by many.

    The intention for [Lisp] was lexical scoping, but the implementation
    used dynamic scoping. ... Eventually Common Lisp (started 1981,
    released 1984) added a separate syntax for lexical scoping to
    mainstream Lisp, but that was more than two decades after dynamically
    scoped Lisp had been implemented and become idiomatic.

    Scheme had lexical scope in the late 1970s and I believe it appeared in
    some Lisps earlier than Common Lisp, but that was before my time.

    Scheme was first in 1975, but it did not become the Lisp mainstream.
    1975 was 15 years after Lisp was introduced in 1960 and probably
    almost as many years after the difference between the intention and
    the implementation was discovered.

    There is also the matter that dynamic
    scope is very easy to implement, so that might have affected what people
    did.

    Common Lisp requires implementing both dynamic scoping (using the '
    syntax) and static scoping (IIRC using the #' syntax). Dynamic
    scoping does not make the implementation of Common Lisp easier. It's
    there because programs were written to work with dynamic scoping. So
    many programs that eliminating it and switching to Scheme was
    impractical.

    Another case is the story of S-expressions vs. (Algol- or ML-like)
    M-expressions in Lisp.

    M-expressions never caught on because Lispers liked S-expressions.

    Exactly. The original idea was that S-expressions are just a stopgap
    until M-expressions are implemented, but they found out that
    programmers preferred S-expressions, so Lisp uses S-expressions to
    this day. Compare this to what happened to call-by-value.

    In any case, call-by-name does not appear in any later languages that
    I have ever heard of.

    https://www.geeksforgeeks.org/scala-functions-call-by-name/

    Interesting. Looking at <https://rosettacode.org/wiki/Jensen%27s_Device#Scala>, we see that
    the call to the term is somewhat Algol-like, but the supposed
    call-by-name is actually restricted like the procedure/PROC modes of
    Algol W and Algol 68: It cannot be used for passing i, and therefore
    some extra work was done to pass i in a way that is modifyable.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to Anton Ertl on Fri Mar 15 18:49:40 2024
    In article <2024Mar15.163313@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    albert@spenarnc.xs4all.nl writes:
    In algol68 the unclean Jensen's device was replaced by references
    once it was realized what it was. This permitted the same code,
    without the mystification.

    Whatever you may mean by "unclean" and "mystification", looking at ><https://rosettacode.org/wiki/Jensen%27s_Device#ALGOL_68> shows the
    header of sum to be

    PROC sum = (REF INT i, INT lo, hi, PROC REAL term)REAL:

    which is then called with

    sum (i, 1, 100, REAL: 1/i)

    So i is passed by REFerence, lo and hi are passed by value (the

    Incorrect. `` REF INT i '' is passed by value.
    Only values are passed to parameters.
    That is the whole crux of algol 68.

    default in Algol 68, while call-by-name is the default in Algol 60),
    and 1/i is passed as PROC (the implementation mechanism behind
    call-by-name). Try using "REF" instead of "PROC", and see that the
    program does not work as intended (if it compiles at all).

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html >comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023
    --
    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 Paul Rubin@21:1/5 to Anton Ertl on Fri Mar 15 11:50:30 2024
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    What I see in the Algol 60, Algol W, Algol 68, and Pascal entries of <https://rosettacode.org/wiki/Jensen's_Device> is a steady progression
    away from call-by-name (and towards call-by-value):

    It sounds like Algol 68 and Pascal (don't know about Algol W) had a way
    to pass procedures as parameters. That was missing from Algol 60 so the
    only way to get that effect was call by name. E.g. if you want to write
    a root finder that finds a zero of some function, how do you pass the
    function?

    It's weird though. Function parameters were straightforward and
    important in FORTRAN, which predated Algol 60, so I'd expect the Algol
    60 designers to have known better. It does sound from Naur's report
    that not all of the Algol 60 committee knew what it was getting into
    with call by name.

    In Algol 68, i is passed by-REFerence, lo and hi are passed by-value
    (the default in Algol 68), and term is passed by-PROCedure (probably
    like the procedure mode of Algol 60).

    Algol 60 had a procedure mode? Hmm, maybe I misunderstood Naur. I'll
    try to look at Rosetta Code.

    Common Lisp requires implementing both dynamic scoping (using the '
    syntax) and static scoping (IIRC using the #' syntax).

    No ' just quotes a sexp and #' quotes a function. Scheme also has
    dynamic scoping using fluid-let.

    Dynamic scoping does not make the implementation of Common Lisp
    easier. It's there because programs were written to work with dynamic scoping. So many programs that eliminating it and switching to Scheme
    was impractical.

    That might be, it was before my time. But I wrote a purely dynamically
    scoped Lisp back in the day, and I studied the internals of Emacs Lisp,
    and dynamic scope is very easy to implement using shallow binding.
    Lexical scope is somewhat harder, though maybe not enough to matter in a compiler.

    [Scala] the supposed call-by-name is actually restricted like the procedure/PROC modes of Algol W and Algol 68: It cannot be used for
    passing i, and therefore some extra work was done to pass i in a way
    that is modifyable.

    It's maybe similar in Haskell, whose lazy evaluation is supposed to be semantically equivalent to call-by-name, but which is memoized, doable
    because mutation is not allowed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Fri Mar 15 23:02:16 2024
    Paul Rubin <no.email@nospam.invalid> writes:
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    What I see in the Algol 60, Algol W, Algol 68, and Pascal entries of
    <https://rosettacode.org/wiki/Jensen's_Device> is a steady progression
    away from call-by-name (and towards call-by-value):

    It sounds like Algol 68 and Pascal (don't know about Algol W) had a way
    to pass procedures as parameters. That was missing from Algol 60 so the
    only way to get that effect was call by name. E.g. if you want to write
    a root finder that finds a zero of some function, how do you pass the >function?

    It's weird though. Function parameters were straightforward and
    important in FORTRAN, which predated Algol 60, so I'd expect the Algol
    60 designers to have known better. It does sound from Naur's report
    that not all of the Algol 60 committee knew what it was getting into
    with call by name.

    In Algol 68, i is passed by-REFerence, lo and hi are passed by-value
    (the default in Algol 68), and term is passed by-PROCedure (probably
    like the procedure mode of Algol 60).

    Algol 60 had a procedure mode?

    No only by-name and by-value. I meant the procedure mode of Algol W.

    [Scala] the supposed call-by-name is actually restricted like the
    procedure/PROC modes of Algol W and Algol 68: It cannot be used for
    passing i, and therefore some extra work was done to pass i in a way
    that is modifyable.

    It's maybe similar in Haskell, whose lazy evaluation is supposed to be >semantically equivalent to call-by-name, but which is memoized, doable >because mutation is not allowed.

    The lazy evaluation of Haskell is certainly not equivalent to
    call-by-name, because you cannot implement Jensen's device with it.
    It's not even like the PROC/procedure mechanism, because it evaluates
    the argument at most once, while a PROC/procedure parameter is
    evaluated repeatedly.

    <https://rosettacode.org/wiki/Jensen%27s_Device#Haskell> does
    something with monads that I don't understand, but it's noticable that
    sum has no parameter i and the code for the actual term parameter does
    not reference i.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Anton Ertl on Mon Mar 18 16:56:44 2024
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    The lazy evaluation of Haskell is certainly not equivalent to
    call-by-name, because you cannot implement Jensen's device with it.

    Jensen's device depends on having mutable variables. If they are not
    allowed, Haskell's evaluation is equivalent to call-by-name.

    <https://rosettacode.org/wiki/Jensen%27s_Device#Haskell> does
    something with monads that I don't understand

    It creates a mutable memory cell (STRef) that is read and written as if
    it's an i/o device (readSTRef/writeSTRef). Then it makes a closure that
    reads from the cell and returns the reciprocal of the contents, and
    sums over the result of that closure when it writes k=1,2...n into the
    cell. Ugh!!!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to no.email@nospam.invalid on Tue Mar 19 11:30:06 2024
    In article <87zfuvxfer.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    The lazy evaluation of Haskell is certainly not equivalent to
    call-by-name, because you cannot implement Jensen's device with it.

    Jensen's device depends on having mutable variables. If they are not >allowed, Haskell's evaluation is equivalent to call-by-name.

    <https://rosettacode.org/wiki/Jensen%27s_Device#Haskell> does
    something with monads that I don't understand

    It creates a mutable memory cell (STRef) that is read and written as if
    it's an i/o device (readSTRef/writeSTRef). Then it makes a closure that >reads from the cell and returns the reciprocal of the contents, and
    sums over the result of that closure when it writes k=1,2...n into the
    cell. Ugh!!!

    It serves in my opinion that Jensen's is serependitious technique that
    is only used as there are no better techniques available.
    There is no discretion at Rosetta, otherwise this example would thrown
    out for being not appropriate for this forum to show techniques.

    Groetjes Albert
    --
    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 minforth@21:1/5 to albert@spenarnc.xs4all.nl on Tue Mar 19 12:43:26 2024
    albert@spenarnc.xs4all.nl wrote:

    In article <87zfuvxfer.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    The lazy evaluation of Haskell is certainly not equivalent to
    call-by-name, because you cannot implement Jensen's device with it.

    Jensen's device depends on having mutable variables. If they are not >>allowed, Haskell's evaluation is equivalent to call-by-name.

    <https://rosettacode.org/wiki/Jensen%27s_Device#Haskell> does
    something with monads that I don't understand

    It creates a mutable memory cell (STRef) that is read and written as if >>it's an i/o device (readSTRef/writeSTRef). Then it makes a closure that >>reads from the cell and returns the reciprocal of the contents, and
    sums over the result of that closure when it writes k=1,2...n into the >>cell. Ugh!!!

    It serves in my opinion that Jensen's is serependitious technique that
    is only used as there are no better techniques available.
    There is no discretion at Rosetta, otherwise this example would thrown
    out for being not appropriate for this forum to show techniques.

    My notion as well. If you look at closures eg in Javascript, it seems that
    the main benefit of closures is the easy handling of private methods and variables without opening a big barrel like OOP.

    The other way round: if you already have OO in your language, you don't
    need closures.

    Jensen's device or Knuth's man-or-boy are just programming playgrounds.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Tue Mar 19 16:47:48 2024
    Paul Rubin <no.email@nospam.invalid> writes:
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    The lazy evaluation of Haskell is certainly not equivalent to
    call-by-name, because you cannot implement Jensen's device with it.

    Jensen's device depends on having mutable variables. If they are not >allowed, Haskell's evaluation is equivalent to call-by-name.

    And if it's required to use every argument, then eager evaluation (call-by-value) is equivalent to Haskell's lazy evaluation.

    But Algol 60 has mutable variables, and, as you point out, Jensen's
    device depends on that, and therefore Haskell's way of argument
    passing is not equivalent to Algol 60's call-by-name.

    <https://rosettacode.org/wiki/Jensen%27s_Device#Haskell> does
    something with monads that I don't understand

    It creates a mutable memory cell (STRef) that is read and written as if
    it's an i/o device (readSTRef/writeSTRef). Then it makes a closure that >reads from the cell and returns the reciprocal of the contents, and
    sums over the result of that closure when it writes k=1,2...n into the
    cell. Ugh!!!

    Yes, something in this direction is what I understood. What is
    unclear to me is how this monad stuff interacts with the lazy
    evaluation.

    The way I have heard about Haskell programming up to now is that one
    tries to have a pure functional part, and then use monads at the
    fringes for things like I/O where pure functional code does not cut
    it. I don't see this kind of separation in the Haskell code above. I
    wonder if there is a more idiomatic way of writing this stuff in
    Haskell.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Tue Mar 19 17:36:44 2024
    minforth@gmx.net (minforth) writes:
    My notion as well. If you look at closures eg in Javascript, it seems that >the main benefit of closures is the easy handling of private methods and >variables without opening a big barrel like OOP.

    The main benefit of closures is that you can pass data to a callback
    that is not provided for by the interface of tha callback. OOP does
    not provide this capability, so "opening a big barrel like OOP" would
    not help.

    As an example, take a look at one of the Jensen's device variants:

    : sum ( i-xt lo hi term-xt -- r )
    \ stack effects: i-xt ( -- addr ); term-xt ( -- r1 )
    0e swap 1+ rot ?do ( r1 xt1 xt2 )
    i 2 pick execute ! dup execute f+
    loop 2drop ;

    No data is passed by SUM to I-XT nor TERM-XT, so the data has to be
    passed in some other way. In

    variable i1 \ avoid conflict with Forth word I
    ' i1 1 100 :noname 1e i1 @ s>f f/ ; sum f.

    a global variable I1 is used for passing the additional data. In

    : main ( -- )
    0 {: w^ i1 :} i1 [n:l ;] 1 100 i1 [n:l @ s>f 1/f ;] sum f. ;

    a variable-flavoured local variable I1 is used, and in

    i1 [n:l ;]

    an xt (of a closure) is created that pushes I1 when executed.
    Likewise

    i1 [n:l @ s>f 1/f ;]

    creates the xt of a closure that computes "i1 @ s>f 1/f" when
    executed.

    The other way round: if you already have OO in your language, you don't
    need closures.

    You cannot do with objects what closures do in the example above.
    Gforth has had objects (in several flavours) for decades, but we added
    closures in 2018 because they add capabilities that objects do not
    provide.

    Jensen's device or Knuth's man-or-boy are just programming playgrounds.

    Sour grapes?

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Tue Mar 19 19:06:41 2024
    Anton Ertl wrote:

    minforth@gmx.net (minforth) writes:
    My notion as well. If you look at closures eg in Javascript, it seems that >>the main benefit of closures is the easy handling of private methods and >>variables without opening a big barrel like OOP.

    The main benefit of closures is that you can pass data to a callback
    that is not provided for by the interface of tha callback.

    Yes, I should have mentioned callbacks too. ISTM only that closures are used more often for encapsulation. But sure, this depends on the application domains.

    OOP does not provide this capability, so "opening a big barrel like OOP" would
    not help.

    Object methods can receive and return function pointers / execution tokens as required for callbacks.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to albert@spenarnc.xs4all.nl on Tue Mar 19 17:57:19 2024
    albert@spenarnc.xs4all.nl writes:
    It serves in my opinion that Jensen's is serependitious technique that
    is only used as there are no better techniques available.

    It's pretty clear from Naur's article that Jensen's trick was
    intentional rather than serependitious. One can say retrospectively
    that it was a mistake because there were better things they could have
    done instead. The committee was split on how to do it, and the wrong
    side won. Despite this error, Algol 60 is still highly regarded, so we
    can say for a language designed by a committee, it did a very good job
    even if one can take issue with some specifics.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Anton Ertl on Tue Mar 19 18:10:11 2024
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    And if it's required to use every argument, then eager evaluation (call-by-value) is equivalent to Haskell's lazy evaluation.

    "required to use every argument" and "strict evaluation" are sort of the
    same thing. The difference between strict and non-strict is what
    happens if some of the args are not used, or only partially consumed.
    With strict evaluation, passing 1/0 as an argument should raise an
    error, and trying to create infinite list will run forever or exhaust
    memory. With non-strict, both of those are ok, and it's only an error
    if you try to actually (e.g.) print the value.

    CBN and lazy evaluation are two different strategies for implementing non-strict evaluation. If I'm using the jargon right, they have the
    same denotational semantics, but different operational semantics.

    The way I have heard about Haskell programming up to now is that one
    tries to have a pure functional part, and then use monads at the
    fringes for things like I/O where pure functional code does not cut
    it. I don't see this kind of separation in the Haskell code above.

    I'll try to think of better explanation for this later, but basically
    monads are just a notational trick, and I think that the Jensen's
    example could have been done with the State monad instead of ST. State
    is purely functional and works by threading a value through a chain of
    function evaluations. ST (State Transformer) does sort of the same
    thing as State, but it lets you use mutable memory cells, which can be
    more efficient.

    I wonder if there is a more idiomatic way of writing this stuff in
    Haskell.

    I don't see one offhand, except maybe with macros or some silly thing
    like that.

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