• RFD: Static forward references

    From Brad Eckert@21:1/5 to All on Sat Aug 12 20:43:20 2023
    The usual way to handle forward references in Forth is to use DEFER and IS. The CORE EXT wordset includes some other goodies like ACTION-OF, DEFER!, and DEFER@.

    This usage provides a kind of OOPless late binding. Dynamic forward references can be changed by DEFER!. What if all you want is a static forward reference? The overhead is a waste of resources.

    I use LATER and RESOLVES:

    LATER foo \ compiles a jump instruction with a blank address
    : bar foo ... ;
    :noname ( foo ) ; RESOLVES foo \ fills in the address

    So, there are some points I would like to make about DEFER:

    1. DEFER is used for two different things. The spirit of Forth is for words to do only one thing. It's overkill for the static case.

    2. IS can be "smartened up", or overloaded the way TO is, but the complexification is anti-Forth even if you can make it work.

    3. There could be better words to use than LATER and RESOLVES for static forward references. I have seen IST used, but we are not all German.

    4. This is almost surely a matter of taste. I don't like to waste RAM and clock cycles. There are plenty of those to burn these days. However, there is room for improvement in CORE EXT. Perhaps support can be added for simple static forward references.

    Comments?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to Brad Eckert on Sun Aug 13 02:06:18 2023
    On Sunday, August 13, 2023 at 5:43:22 AM UTC+2, Brad Eckert wrote:
    The usual way to handle forward references in Forth is to use DEFER and IS. The CORE EXT wordset includes some other goodies like ACTION-OF, DEFER!, and DEFER@.

    This usage provides a kind of OOPless late binding. Dynamic forward references can be changed by DEFER!. What if all you want is a static forward reference? The overhead is a waste of resources.

    I use LATER and RESOLVES:

    LATER foo \ compiles a jump instruction with a blank address
    : bar foo ... ;
    :noname ( foo ) ; RESOLVES foo \ fills in the address

    I agree with your comments.

    LMI PC/FORTH had F: and R: for this purpose. I found these commands very easy to memorize and to use. F: defines a forward reference, and R: resolves it. Example:

    F: MENU

    (other definitions)

    R: MENU (here comes the actual code) ;

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Brad Eckert on Sun Aug 13 08:31:11 2023
    Brad Eckert <hwfwguy@gmail.com> writes:
    The usual way to handle forward references in Forth is to use DEFER and IS. The CORE EXT wordset includes some other goodies like ACTION-OF, DEFER!, and DEFER@.

    This usage provides a kind of OOPless late binding. Dynamic forward references can be changed by DEFER!. What if all you want is a static forward reference? The overhead is a waste of resources.

    I use LATER and RESOLVES:

    LATER foo \ compiles a jump instruction with a blank address
    : bar foo ... ;
    :noname ( foo ) ; RESOLVES foo \ fills in the address

    Gforth (development) has FORWARD:

    forward foo
    : bar foo ;
    : foo ." foo" ;

    This does not exist because there was a pressing need for it, but
    because we found a cute way to implement it. You can read about the implementation in
    <http://www.complang.tuwien.ac.at/forth/forward.txt>.

    So, there are some points I would like to make about DEFER:

    1. DEFER is used for two different things. The spirit of Forth is for words to do only one thing. It's overkill for the static case.

    Another design principle in Forth is to avoid unnecessary complexity.
    DEFER ... IS can be used to do what LATER ... RESOLVES do. Are there
    really enough (and important enough) cases to justify the extra
    complexity of LATER...RESOLVES or FORWARD?

    4. This is almost surely a matter of taste. I don't like to waste RAM and clock cycles.

    Concerning RAM, the implementation of LATER...RESOLVES (and FORWARD)
    has an up-front cost. How many DEFER definitions do you have to
    replace with LATER definitions until you have amortized that cost? In
    case of FORWARD, a FORWARD definition consumes two cells more than a
    DEFER definition, and LIT-PERFORM (what Gforth compiles when compiling
    a deferred word) is shorter than CALL (what Gforth compiles when
    compiling a colon definition), so FORWARD does not save RAM in Gforth
    under any circumstances.

    Concerning cycles, yes, using FORWARD instead of DEFER reduces the
    number of instructions executed for the word, and probably also the
    number of cycles, but is it really significant? There are few cases
    of mutual recursion, and even if one of them dominates the run-time,
    how much of it is spent on the code for the deferred or forwarded
    call?

    On sophisticated CPUs, there is little performance difference between
    a direct call and an indirect call that always calls the same target.

    There are plenty of those to burn these days. However, there is room for improvement in CORE EXT. Perhaps support can be added for simple static forward references.

    If you want to propose an extension to the standard, the current way
    to do this is to add a proposal at
    <https://forth-standard.org/proposals>.


    Comments?

    --
    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 none) (albert@21:1/5 to hwfwguy@gmail.com on Sun Aug 13 12:15:46 2023
    In article <aa585f45-b229-4a0a-a304-fd8233dbbeb8n@googlegroups.com>,
    Brad Eckert <hwfwguy@gmail.com> wrote:
    The usual way to handle forward references in Forth is to use DEFER and
    IS. The CORE EXT wordset includes some other goodies like ACTION-OF,
    DEFER!, and DEFER@.

    This usage provides a kind of OOPless late binding. Dynamic forward >references can be changed by DEFER!. What if all you want is a static
    forward reference? The overhead is a waste of resources.

    I use LATER and RESOLVES:

    LATER foo \ compiles a jump instruction with a blank address
    : bar foo ... ;
    :noname ( foo ) ; RESOLVES foo \ fills in the address

    So, there are some points I would like to make about DEFER:

    1. DEFER is used for two different things. The spirit of Forth is for
    words to do only one thing. It's overkill for the static case.

    2. IS can be "smartened up", or overloaded the way TO is, but the >complexification is anti-Forth even if you can make it work.

    3. There could be better words to use than LATER and RESOLVES for static >forward references. I have seen IST used, but we are not all German.

    4. This is almost surely a matter of taste. I don't like to waste RAM
    and clock cycles. There are plenty of those to burn these days. However, >there is room for improvement in CORE EXT. Perhaps support can be added
    for simple static forward references.

    I'm currently implementing MAL (make another lisp).
    In those circumstances mutual recursive functions are common,
    so it really depends. The proposal is to make a lisp in 10 steps.
    You suddenly find that certain words must become mutual referring.

    The solution is to have a dummy header ( and headers are needed
    any way, however solution you may choose.)
    An example using the infamous Fibonacci sequence.
    :F FIB ; \ Forward
    :R DUP IF DUP FIB SWAP 1- FIB + ELSE 0 THEN ; \ Resolve
    The notation is unobtrusive, and why shouldn't it?

    Crafting MAL, you are bound to discover that some words are
    used in unexpected places. It sufficed to add
    :F eval-item ;
    and then fix the actual definition from
    : eval-item ;
    to
    :R eval-item .... ;

    The implementation is not complicated and share one screen with:
    \ Alias of : , redefine an existing(!) word. Or crash.
    : :2 ... ;
    and
    \ Alias of :, define a word that inlines it code.
    : :I ... ;


    Comments?

    My idea is the same, but I like my notation better.

    In an indirect threaded Forth (like ciforth-family) there is no
    penalty for this mechanism. In the end the two headers ("dictionary
    entrie") are the same. After compilation is finished there is
    as it were an extra header, that placed up front.
    The patching that takes place in :R is to fill in a pointer to high
    level core.
    After compilation the code is static.

    Vectors, that can be changed to adapt the behaviour of a program, so
    writing to a file instead of writing to a terminal, is well served by
    IS DEFER. I agree with you that that is a totally different
    functionality.
    Changing the behaviour of a compiled program *afterwards" is totally
    different from recursion or mutual recursion.

    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 dxforth@21:1/5 to Anton Ertl on Sun Aug 13 20:51:23 2023
    On 13/08/2023 6:31 pm, Anton Ertl wrote:
    Brad Eckert <hwfwguy@gmail.com> writes:
    The usual way to handle forward references in Forth is to use DEFER and IS. The CORE EXT wordset includes some other goodies like ACTION-OF, DEFER!, and DEFER@.

    This usage provides a kind of OOPless late binding. Dynamic forward references can be changed by DEFER!. What if all you want is a static forward reference? The overhead is a waste of resources.

    I use LATER and RESOLVES:

    LATER foo \ compiles a jump instruction with a blank address
    : bar foo ... ;
    :noname ( foo ) ; RESOLVES foo \ fills in the address

    Gforth (development) has FORWARD:

    forward foo
    : bar foo ;
    : foo ." foo" ;

    This does not exist because there was a pressing need for it, but
    because we found a cute way to implement it. You can read about the implementation in
    <http://www.complang.tuwien.ac.at/forth/forward.txt>.

    So, there are some points I would like to make about DEFER:

    1. DEFER is used for two different things. The spirit of Forth is for words to do only one thing. It's overkill for the static case.

    Another design principle in Forth is to avoid unnecessary complexity.
    DEFER ... IS can be used to do what LATER ... RESOLVES do. Are there
    really enough (and important enough) cases to justify the extra
    complexity of LATER...RESOLVES or FORWARD?

    About the same as DEFER@ DEFER! ACTION-OF BUFFER: etc

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Marcel Hendrix@21:1/5 to Brad Eckert on Sun Aug 13 07:50:32 2023
    On Sunday, August 13, 2023 at 5:43:22 AM UTC+2, Brad Eckert wrote:
    [..]
    1. DEFER is used for two different things. The spirit of Forth is for words to do only one thing.
    [..]
    Maybe that statement is incomplete? See COUNT ( c@+ ), or 2>R ( DO ), or */MOD ( *, /, mod).
    Good words do their best to be as low-level / general as possible. They can, and are, used wildly
    out of context in some cases, where somebody renamed them to point out a useful re-purpose.
    These two characteristics together make it natural to sequence a few of these primitives
    together and give them a descriptive name that works better (for the user) in the context at
    hand. The best names are ambiguous to make these sequences work in many contexts
    (the same idea as in spoken languages).
    It should be "COUNT," not "parse-string-descriptor_256."

    -marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Marcel Hendrix on Mon Aug 14 12:38:49 2023
    On 14/08/2023 12:50 am, Marcel Hendrix wrote:
    On Sunday, August 13, 2023 at 5:43:22 AM UTC+2, Brad Eckert wrote:
    [..]
    1. DEFER is used for two different things. The spirit of Forth is for words to do only one thing.
    [..]
    Maybe that statement is incomplete? See COUNT ( c@+ ), or 2>R ( DO ), or */MOD ( *, /, mod).
    Good words do their best to be as low-level / general as possible. They can, and are, used wildly
    out of context in some cases, where somebody renamed them to point out a useful re-purpose.
    These two characteristics together make it natural to sequence a few of these primitives
    together and give them a descriptive name that works better (for the user) in the context at
    hand. The best names are ambiguous to make these sequences work in many contexts
    (the same idea as in spoken languages).
    It should be "COUNT," not "parse-string-descriptor_256."

    Speaking of which ANS hoped the "-FILE" words would one day be shortened. They're
    certainly an eye-sore. Likely too late now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From none) (albert@21:1/5 to hheinrich.hohl@gmail.com on Mon Aug 14 09:53:40 2023
    In article <06376332-0029-4996-88d1-3e80897f2aecn@googlegroups.com>,
    Heinrich Hohl <hheinrich.hohl@gmail.com> wrote:
    On Sunday, August 13, 2023 at 5:43:22 AM UTC+2, Brad Eckert wrote:
    The usual way to handle forward references in Forth is to use DEFER
    and IS. The CORE EXT wordset includes some other goodies like ACTION-OF, >DEFER!, and DEFER@.

    This usage provides a kind of OOPless late binding. Dynamic forward >references can be changed by DEFER!. What if all you want is a static
    forward reference? The overhead is a waste of resources.

    I use LATER and RESOLVES:

    LATER foo \ compiles a jump instruction with a blank address
    : bar foo ... ;
    :noname ( foo ) ; RESOLVES foo \ fills in the address

    I agree with your comments.

    LMI PC/FORTH had F: and R: for this purpose. I found these commands very easy >to memorize and to use. F: defines a forward reference, and R: resolves it. >Example:

    F: MENU

    (other definitions)

    R: MENU (here comes the actual code) ;

    I have :F and :R. Damn!

    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 Heinrich Hohl@21:1/5 to Anton Ertl on Mon Aug 14 01:15:54 2023
    On Sunday, August 13, 2023 at 11:34:49 AM UTC+2, Anton Ertl wrote:
    Another design principle in Forth is to avoid unnecessary complexity.
    DEFER ... IS can be used to do what LATER ... RESOLVES do. Are there
    really enough (and important enough) cases to justify the extra
    complexity of LATER...RESOLVES or FORWARD?

    In my opinion the main benefit of forwarding/resolving a colon definition (instead of DEFER and IS) is clarity of code. In the first case we have
    static behavior, in the second case a self-modifying program.

    A similar case where we introduced two different words for almost the same
    task are CONSTANT and VALUE

    CONSTANT is not really needed if we have VALUE at hand because we could store any
    constant number using VALUE just as well. But if we use CONSTANT in this case we point out clearly that the stored number is not supposed to be changed anywhere in the program.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From jan Coombs@21:1/5 to Heinrich Hohl on Mon Aug 14 10:52:13 2023
    On Mon, 14 Aug 2023 01:15:54 -0700 (PDT)
    Heinrich Hohl <hheinrich.hohl@gmail.com> wrote:

    In my opinion the main benefit of forwarding/resolving a colon definition (instead of DEFER and IS) is clarity of code. In the first case we have static behavior, in the second case a self-modifying program.

    A similar case where we introduced two different words for almost the same task are CONSTANT and VALUE

    Agreed, I also prefer words to be intention-explicit, even when code is same.

    Jan Coombs
    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Heinrich Hohl on Mon Aug 14 20:50:09 2023
    On 14/08/2023 6:15 pm, Heinrich Hohl wrote:
    On Sunday, August 13, 2023 at 11:34:49 AM UTC+2, Anton Ertl wrote:
    Another design principle in Forth is to avoid unnecessary complexity.
    DEFER ... IS can be used to do what LATER ... RESOLVES do. Are there
    really enough (and important enough) cases to justify the extra
    complexity of LATER...RESOLVES or FORWARD?

    In my opinion the main benefit of forwarding/resolving a colon definition (instead of DEFER and IS) is clarity of code. In the first case we have static behavior, in the second case a self-modifying program.

    A similar case where we introduced two different words for almost the same task are CONSTANT and VALUE

    CONSTANT is not really needed if we have VALUE at hand because we could store any
    constant number using VALUE just as well. But if we use CONSTANT in this case we point out clearly that the stored number is not supposed to be changed anywhere in the program.

    Changing a CONSTANT amounts to a hot patch. VALUE and DEFER are
    alike in that the thing that changes is implicitly VARIABLE.
    IIRC the only things in Forth that perform a hot patch are DOES>
    and IMMEDIATE and are disliked for that reason.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Heinrich Hohl on Mon Aug 14 14:03:10 2023
    Heinrich Hohl <hheinrich.hohl@gmail.com> writes:
    On Sunday, August 13, 2023 at 11:34:49=E2=80=AFAM UTC+2, Anton Ertl wrote:
    Are there=20
    really enough (and important enough) cases to justify the extra=20
    complexity of LATER...RESOLVES or FORWARD?
    ...
    A similar case where we introduced two different words for almost the same >task are CONSTANT and VALUE

    Yes, we could have used VALUE instead of CONSTANT, and if VALUE had
    been introduced first, maybe we would never have introduced CONSTANT.

    However, looking at the occurences in the sources of Gforth's image
    (without kernel; building the kernel uses the cross-compiler, with its
    own instances of defining words), there is one big difference between
    these two situations:

    occurences read-only read/write
    CONSTANT/VALUE 162 33
    FORWARD/DEFER 0 40

    Admittedly FORWARD is recent, so maybe some cases are covered by DEFER
    that are actually hidden FORWARDs (but no such case comes to my mind).
    Also, there are forward references in the kernel which are resolved automatically by the cross-compiler.

    - 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 Hans Bezemer@21:1/5 to Brad Eckert on Tue Aug 15 16:06:18 2023
    On Sunday, August 13, 2023 at 5:43:22 AM UTC+2, Brad Eckert wrote:
    The usual way to handle forward references in Forth is to use DEFER and IS. The CORE EXT wordset includes some other goodies like ACTION-OF, DEFER!, and DEFER@.

    This usage provides a kind of OOPless late binding. Dynamic forward references can be changed by DEFER!. What if all you want is a static forward reference? The overhead is a waste of resources.

    I use LATER and RESOLVES:

    LATER foo \ compiles a jump instruction with a blank address
    : bar foo ... ;
    :noname ( foo ) ; RESOLVES foo \ fills in the address

    So, there are some points I would like to make about DEFER:

    1. DEFER is used for two different things. The spirit of Forth is for words to do only one thing. It's overkill for the static case.

    2. IS can be "smartened up", or overloaded the way TO is, but the complexification is anti-Forth even if you can make it work.

    3. There could be better words to use than LATER and RESOLVES for static forward references. I have seen IST used, but we are not all German.

    4. This is almost surely a matter of taste. I don't like to waste RAM and clock cycles. There are plenty of those to burn these days. However, there is room for improvement in CORE EXT. Perhaps support can be added for simple static forward references.

    Comments?
    4tH has got PROTO: <name> for defining it and :PROTO <name> for resolving it. The former creates two jumps.
    The first one jumps over the second, blank one - and is the target for the calls to it.

    The later patches the blank jump address to match its own. It also makes sure that subsequent calls
    go to the true address, rather than the trampoline.

    Performance wise the difference with DEFER is negligable. It tend to use DEFER when the program
    must or can change its target word. A static forward is done with :PROTO. I try to cut down on my
    data consumption ;-)

    Hans Bezemer

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Hans Bezemer on Wed Aug 16 13:45:02 2023
    On 16/08/2023 9:06 am, Hans Bezemer wrote:
    ...
    Performance wise the difference with DEFER is negligable. It tend to use DEFER when the program
    must or can change its target word. A static forward is done with :PROTO. I try to cut down on my
    data consumption ;-)

    This raises the question of what embedded systems use - particularly those claiming allegiance to ANS. I notice ANS target compilers are quite smart
    when it comes to CREATE DOES>. Is there anything that prevents them being smart about DEFER IS ?

    If embedded concepts are to be included in Standard Forth, it deserves its
    own section - if only to keep sanity. It's where I'd put BUFFER: assuming
    it was ever needed.

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