• Operator =?UTF-8?B?b3ZlcmxvYWRpbmc/?=

    From minforth@21:1/5 to All on Thu Jul 25 07:30:58 2024
    Forth has a lot of ‘redundant’ operators for e.g. arithmetic or
    stack operations, depending on the data type.

    There was once an interesting approach for a type-bound layer
    on top of standard Forth: https://comp.lang.forth.narkive.com/rexLEBd0/strongforth-implemented-in-ans-forth

    Unfortunately, the website for downloading strongforth.f
    is no longer available.

    Or has anyone found another way to bundle/overload Forth operators?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From mhx@21:1/5 to All on Thu Jul 25 09:06:03 2024
    DO you mean something like the below? The code stays the
    same but it is possible to choose a different type of
    variable (from BYTE to arbitrary precision).

    -marcel

    (*
    * LANGUAGE : ANS Forth with extensions
    * PROJECT : Forth Environments
    * DESCRIPTION : Hairy floating-point issues
    * CATEGORY : Demonstration, using of OPG (see also archimedes.frt)
    * AUTHOR : Marcel Hendrix
    * LAST CHANGE : August 4, 2012, Marcel Hendrix
    * LAST CHANGE : Saturday, December 22, 2012, 22:46, Marcel Hendrix
    *)


    NEEDS -xopg

    REVISION -archimedes "--- Archimedes' PI Version 2.01 ---"

    PRIVATES

    DOC
    (*
    Round-off error can affect the convergence and accuracy of iterative
    numerical procedures. As an example, Archimedes approximated PI by
    calculating the perimeters of polygons inscribing and circumscribing
    a circle, starting with hexagons, and successively doubling the number
    of sides. As noted above, computations may be rearranged in a way that
    is mathematically equivalent but less prone to error (numerical analysis).
    Two forms of the recurrence formula for the circumscribed polygon are:

    t0 = 1 / sqrt(3)

    sqrt(ti^2+1) - 1
    first form: ti+1 = ----------------
    ti

    ti
    second form: ti+1 = ----------------
    sqrt(ti^2+1) + 1

    *)
    ENDDOC

    \ .xfloat.p
    .arbitrary.p

    : #SHOW ( -- )
    0-VALUE GLOCAL t0
    PI-VALUE GLOCAL pi
    LET SQRT(1/3): GLOCAL ti1
    LET SQRT(1/3): GLOCAL ti2

    #129 0 DO
    I #15 AND
    0= IF
    CR I 3 .R SPACE
    LET t0=6*ti1*2^DOUBLE[I]:
    LET (t0-PI,t0): .PRINT ." (err=" .PRINT ." )"
    CR 4 SPACES
    LET t0=6*ti2*2^DOUBLE[I]:
    LET (t0-PI,t0): .PRINT ." (err=" .PRINT ." )"
    ENDIF
    LET ti1 = (SQRT(ti1*ti1+1)-1)/ti1:
    LET ti2 = ti2/(SQRT(ti2*ti2+1)+1):
    LOOP ;

    :ABOUT CR ." Try: #SHOW -- approximates PI using two different
    algorithms."
    CR ." PI = " PI-VALUE .PRINT ;

    .ABOUT -archimedes CR
    DEPRIVE

    (* End of Source *)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Thu Jul 25 08:21:12 2024
    Thanks. But it seems this is only the old 16-bit DOS version.
    I have been looking for the ANS-compatible layer file
    mentioned in the discussion.

    IMHO and without belittling strongforth's merits, I think
    that it went too far and proposed another Forth dialect.

    I am thinking on a much smaller scale i.e. unification of
    operators for xVALUEs and xLOCALs. ANS Forth already has
    overloaded TO but stops there.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Thu Jul 25 14:38:11 2024
    On Thu, 25 Jul 2024 12:08:58 +0000, Anton Ertl wrote:

    minforth@gmx.net (minforth) writes:
    Thanks. But it seems this is only the old 16-bit DOS version.

    Looking at the Vierte Dimension 2/2024, which arrived this week, I
    find another article about the new, 32-bit strongForth, and also the
    link

    https://www.stephan-becher.de/strongforth3/


    Wow!! Thanks for the link. And kudos to Stephan Becher!!

    First I'll have to digest this a bit more...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to mhx on Thu Jul 25 14:49:55 2024
    On Thu, 25 Jul 2024 9:06:03 +0000, mhx wrote:

    DO you mean something like the below? The code stays the
    same but it is possible to choose a different type of
    variable (from BYTE to arbitrary precision).

    Thank you! Yes, something like that.

    If I understand correctly, the intelligence is within LET
    which decides which operator matches the type of the operands.

    I have also dynamic vector and array types. And 24-bit floats..
    The other discussion in the automatic differentiation thread
    introduces dual fp-numbers, etc. Overall, working with many
    different types would explode the number of type-dependent
    operators. So it becomes a natural idea to smarten up the
    compiler in this respect.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Fri Jul 26 08:33:41 2024
    On Thu, 25 Jul 2024 9:06:03 +0000, mhx wrote:

    DO you mean something like the below? The code stays the
    same but it is possible to choose a different type of
    variable (from BYTE to arbitrary precision).
    ...
    0-VALUE GLOCAL t0
    LET SQRT(1/3): GLOCAL ti1
    LET SQRT(1/3): GLOCAL ti2
    ...
    LET t0=6*ti1*2^DOUBLE[I]:

    I gather the (integer) 6 is autoconverted. Why not I as well?

    LET ti1 = (SQRT(ti1*ti1+1)-1)/ti1:
    LET ti2 = ti2/(SQRT(ti2*ti2+1)+1):

    Thank you! Yes, something like that.

    If I understand correctly, the intelligence is within LET
    which decides which operator matches the type of the operands.

    Can you use global VALUEs (or even VARIABLEs) within LET?
    Or does it only work with local types?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From mhx@21:1/5 to minforth on Fri Jul 26 11:25:35 2024
    On Fri, 26 Jul 2024 8:33:41 +0000, minforth wrote:

    On Thu, 25 Jul 2024 9:06:03 +0000, mhx wrote:

    [..]

    LET t0=6*ti1*2^DOUBLE[I]:

    I gather the (integer) 6 is autoconverted. Why not I as well?

    The problem is that I must be understood as an integer, not a
    float, as it would be by default. It probably works fine without
    double[] here. Functions with mixed types are a nuisance.

    The '6' will become a float by default and that is just what is
    needed.

    [..]

    If I understand correctly, the intelligence is within LET
    which decides which operator matches the type of the operands.

    LET is the XOPG entry word and converts standard formulas
    to Forth with the asked for floating or integer numeric precision.

    Can you use global VALUEs (or even VARIABLEs) within LET?
    Or does it only work with local types?

    It works with variables, values, and locals of any type. Arrays
    of standard & iForth types are also supported.
    That it can work with locals is actually the unique feature
    here :--)

    LET is not written in standard Forth. It is based on
    Wil Baden's OPG (Operator Precedence Grammar), but there are
    additions so that XOPG can be used as a SPICE expression
    evaluator/compiler.

    -marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to mhx on Fri Jul 26 15:32:37 2024
    On Fri, 26 Jul 2024 11:25:35 +0000, mhx wrote:
    LET is not written in standard Forth. It is based on
    Wil Baden's OPG (Operator Precedence Grammar), but there are
    additions so that XOPG can be used as a SPICE expression
    evaluator/compiler.

    Okay, thanks! I found OPG here
    http://www.wilbaden.com/neil_bawd/opg.txt

    Interesting concept. I knew only of Julian Noble's ftran.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Sat Jul 27 12:10:28 2024
    On Thu, 25 Jul 2024 12:08:58 +0000, Anton Ertl wrote:
    I am thinking on a much smaller scale i.e. unification of
    operators for xVALUEs and xLOCALs. ANS Forth already has
    overloaded TO but stops there.

    +TO is a common extension.

    Certainly. I have some array operations in mind, but they
    are not relevant here. The principle is like

    : INIT { a }
    [[: a ;]] \ read counter
    [[: 1 +to a ;]] ; \ increment counter
    DEFER count IS count
    DEFER read IS read
    5 INIT
    COUNT COUNT READ -> should give 7

    [[: ;]] define closures, but unlike gforth's more flexible
    flat closures, they capture simply all upvalues (here local a).

    The code compiles unmodified for different types of a.
    Incidentally, it compiles now with +TO, but realistically you
    can't declare new 'op'TOs for too many different 'op' operators.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Sat Jul 27 15:35:55 2024
    minforth@gmx.net (minforth) writes:
    On Thu, 25 Jul 2024 12:08:58 +0000, Anton Ertl wrote:
    I am thinking on a much smaller scale i.e. unification of
    operators for xVALUEs and xLOCALs. ANS Forth already has
    overloaded TO but stops there.

    +TO is a common extension.

    Certainly. I have some array operations in mind, but they
    are not relevant here. The principle is like

    : INIT { a }
    [[: a ;]] \ read counter
    [[: 1 +to a ;]] ; \ increment counter
    DEFER count IS count
    DEFER read IS read
    5 INIT
    COUNT COUNT READ -> should give 7

    My guess is that three lines here should be

    DEFER count
    DEFER read
    5 INIT IS count IS read

    [[: ;]] define closures, but unlike gforth's more flexible
    flat closures, they capture simply all upvalues (here local a).

    Gforth's flag closures can be considered lower-level (they come out of implementation ideas in the Scheme community) and are easier to
    implement, but emulating closures that capture outer locals is more
    cumbersome. One other aspect is that in Gforth's closures the
    programmer decides whether closures are allocated in the dictionary,
    on the locals stack, on the heap, or elsewhere. In the present case
    the dictionary seems to be a good place, and one can write this
    example as:

    : init ( a -- )
    align here >r , \ allocate a on the heap
    r@ [n:d @ ;] \ read counter
    r> [n:d 1 swap +! ;] \ increment counter
    ;
    DEFER count
    DEFER read
    5 INIT IS count IS read
    count count read . \ prints "7 "

    INIT uses pure-stack closures [n:d consumes a value (in this exampl,
    the address of the cell containing A) from the stack at closure
    creation time and pushes it on the stack at closure run-time. Look,
    Ma, no locals:-).

    However, this does not use value-flavoured stuff, because we have to
    pass the address of A around, and then it's easier to use the variable-flavoured words. However, if you prefer the value-flavoured
    words, value-flavoured fields were recently added to Gforth and can be
    used to do that:

    begin-structure counter
    value: counter-val
    end-structure
    : init ( a -- )
    align here >r counter allot
    r@ to counter-val
    r@ [n:d counter-val ;]
    r> [n:d 1 swap +to counter-val ;]
    ;
    DEFER count
    DEFER read
    5 INIT IS count IS read
    count count read . \ prints "7 "

    However, you still have to deal with the address explicitly, which
    becomes especially obvious in the counter closure.

    This raises the question of why you want to use closures for this
    task. Why not use one of the object-oriented Forth packages, some of
    which support value-flavoured fields (Mini-OOF2 among them AFAIK).

    The code compiles unmodified for different types of a.
    Incidentally, it compiles now with +TO, but realistically you
    can't declare new 'op'TOs for too many different 'op' operators.

    This statement can be read in two different ways. I think there can
    be too many, but Stephen Pelc may think otherwise:-).

    In any case, if the reason you want value-flavoured stuff is that you
    want to be able to change the types, then yes, value-flavoured fields
    (in objects or plain structures) are the way to go.

    One caveat that we have here is that TO (and, AFAIK in existing implementations, +TO) have different stack effects for VALUEs,
    2VALUEs, and FVALUEs, which means that you usually cannot change the
    types between the types that deal with one cell on the stack (VALUE:
    CVALUE: WVALUE: LVALUE: etc.) and those that take two cells or a float
    value.

    - 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 2024: https://euro.theforth.net

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Sat Jul 27 18:16:20 2024
    On Sat, 27 Jul 2024 15:35:55 +0000, Anton Ertl wrote:
    minforth@gmx.net (minforth) writes:
    [[: ;]] define closures, but unlike gforth's more flexible
    flat closures, they capture simply all upvalues (here local a).

    Gforth's flag closures can be considered lower-level (they come out of implementation ideas in the Scheme community) and are easier to
    implement, but emulating closures that capture outer locals is more cumbersome.

    In MF36 quotations already have read/write access to the locals of the
    parent function. So closures became a simple by-product through
    capturing
    the parent's locals stack and injecting it at startup during closure
    runtime. When the closure finishes, the captured stack is simply
    updated.

    I don't know if this would be good enough for a Lisp/Scheme programmer,
    but it works for my needs. And look Ma, no garbage collection. :-)

    One other aspect is that in Gforth's closures the
    programmer decides whether closures are allocated in the dictionary,
    on the locals stack, on the heap, or elsewhere.

    In MF36 it is only the locals stack. AFAIU in other programming
    languagues there are closures where the parent function can be seen as constructor. IOW call the parent function twice and you get different xts/addresses. From this perspective MF36 closures would perhaps only
    qualify as 'half-closures'.

    This raises the question of why you want to use closures for this
    task. Why not use one of the object-oriented Forth packages, some of
    which support value-flavoured fields (Mini-OOF2 among them AFAIK).

    MF36 VALUEs and LOCALs are already built with method VTs. TO et al
    just call the method in the correct method slot. You don't need to
    program type-specific TO +TO *TO. For example

    4 XTBUFFER: _VZMETHODS Z@ Z! Z+ Z* \ methods table for complex values
    : ZVALUE \ ( r: r1 r2 <name> -- ) complex value
    _vzmethods _(value) f, f, ;

    Similarly for xVALUEs of all other data types, including strings and
    matrices.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to Anton Ertl on Sat Jul 27 22:55:48 2024
    In article <2024Jul27.173555@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    minforth@gmx.net (minforth) writes:
    On Thu, 25 Jul 2024 12:08:58 +0000, Anton Ertl wrote:
    I am thinking on a much smaller scale i.e. unification of
    operators for xVALUEs and xLOCALs. ANS Forth already has
    overloaded TO but stops there.

    +TO is a common extension.

    Certainly. I have some array operations in mind, but they
    are not relevant here. The principle is like

    : INIT { a }
    [[: a ;]] \ read counter
    [[: 1 +to a ;]] ; \ increment counter
    DEFER count IS count
    DEFER read IS read
    5 INIT
    COUNT COUNT READ -> should give 7

    My guess is that three lines here should be

    DEFER count
    DEFER read
    5 INIT IS count IS read

    [[: ;]] define closures, but unlike gforth's more flexible
    flat closures, they capture simply all upvalues (here local a).

    Gforth's flag closures can be considered lower-level (they come out of >implementation ideas in the Scheme community) and are easier to
    implement, but emulating closures that capture outer locals is more >cumbersome. One other aspect is that in Gforth's closures the
    programmer decides whether closures are allocated in the dictionary,
    on the locals stack, on the heap, or elsewhere. In the present case
    the dictionary seems to be a good place, and one can write this
    example as:

    : init ( a -- )
    align here >r , \ allocate a on the heap
    r@ [n:d @ ;] \ read counter
    [n:d 1 swap +! ;] \ increment counter
    ;
    DEFER count
    DEFER read
    5 INIT IS count IS read
    count count read . \ prints "7 "

    INIT uses pure-stack closures [n:d consumes a value (in this exampl,
    the address of the cell containing A) from the stack at closure
    creation time and pushes it on the stack at closure run-time. Look,
    Ma, no locals:-).

    However, this does not use value-flavoured stuff, because we have to
    pass the address of A around, and then it's easier to use the >variable-flavoured words. However, if you prefer the value-flavoured
    words, value-flavoured fields were recently added to Gforth and can be
    used to do that:

    begin-structure counter
    value: counter-val
    end-structure
    : init ( a -- )
    align here >r counter allot
    r@ to counter-val
    r@ [n:d counter-val ;]
    [n:d 1 swap +to counter-val ;]
    ;
    DEFER count
    DEFER read
    5 INIT IS count IS read
    count count read . \ prints "7 "

    However, you still have to deal with the address explicitly, which
    becomes especially obvious in the counter closure.

    This raises the question of why you want to use closures for this
    task. Why not use one of the object-oriented Forth packages, some of
    which support value-flavoured fields (Mini-OOF2 among them AFAIK).

    The code compiles unmodified for different types of a.
    Incidentally, it compiles now with +TO, but realistically you
    can't declare new 'op'TOs for too many different 'op' operators.

    This statement can be read in two different ways. I think there can
    be too many, but Stephen Pelc may think otherwise:-).

    Also Marcel Hendrix thinks that. I count a dozen opTO's in tforth
    and iforth.
    Personnaly I hate TO and +TO, but if you decide to use them, you
    could as well go all the way.


    - anton

    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 Mon Jul 29 07:22:18 2024
    On Sat, 27 Jul 2024 20:55:48 +0000, albert@spenarnc.xs4all.nl wrote:
    Personnaly I hate TO and +TO, but if you decide to use them, you
    could as well go all the way.

    I know what you mean. For global values I follow you.

    For locals it becomes annoying IMO to readjust operators in the
    code body to changed data types. Contrived dumb example

    \ for single item
    : TT { f: dat -- res } <- local type: f:
    pi dat f* ; <- suitable operator: f*
    \ for vector/matrix
    : TT { m: dat -- res } <- local type: m:
    pi dat m.* ; <- suitable operator: m.*

    With TO
    : TT { <type>: dat -- res } <- replace <type> with suitable type id
    pi *to dat dat ; <- no adjustment required, no errors

    While this example may look dumb, things look different when you
    have many non-trivial words.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Stephen Pelc on Tue Jul 30 18:55:55 2024
    On Tue, 30 Jul 2024 15:16:28 +0000, Stephen Pelc wrote:
    The standard suggests/specifies that operators such as TO behave as if
    they parse. Ignore that for the moment and define

    variable operator

    : to 1 operator ! ; immediate

    A child of VALUE is probably an immediate word that inspects OPERATOR
    and compiles the fetch action if 0 or the store action if 1. This scheme
    can
    be extended to support a wide range of operators, such as +TO INCR DECR
    and so on. MPE has used this scheme for several decades with no tech
    support issues. If you really want to to be fussy and avoid the use of
    the
    "as if" rule for parsing, you can do something like (untested) the below
    for TO. But why bother?

    : to
    1 operator ! ' execute
    ; immediate

    Thank you. So instead of wasting time on parsing during compilation- or interpretation time, the runtime action of an xVALUE (even when
    compiled)
    involves to walk a type-specific operator chain (a large CASE construct
    in VFX). This makes them slower when compiled, but why not.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From mhx@21:1/5 to Gerry Jackson on Wed Jul 31 09:41:23 2024
    On Wed, 31 Jul 2024 9:08:43 +0000, Gerry Jackson wrote:
    [..]
    111 value x x . 111 ok
    222 to cr .( Does TO parse? ) x x 222 = [if] .( No it doesn't!) [then]
    Does TO parse? No it doesn't! ok
    [..]

    As does iForth.

    You could argue that it's not a standard program because it contains a deliberate ambiguous condition so a parsing TO would fail in some way
    but it does demonstrate non-compliant behaviour.

    222 TO cr

    should (I hope!) produce an exception (unless CR is redefined),
    so this *definitely* fails and doesn't even finish the test.

    I'm not sure that you can use a buggy program to test for an ambiguous condition (looks like a top job for an eager lawyer). It would be
    much better if the anomaly can be shown with a valid program. What's
    the damage when a intentionally erroneous piece of code fails?
    Also note that this program's output is correct on Vfx and fails with
    an exception on compliant Forths :--)

    -marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From mhx@21:1/5 to minforth on Wed Jul 31 13:15:55 2024
    On Wed, 31 Jul 2024 10:31:29 +0000, minforth wrote:

    1 value fv
    222 to cr
    fv

    looks weird but probably works with a non-parsing TO i.e.
    store 222 in fv.

    TO stores an id as a message in a global variable which is
    queried and reset by the next value.

    It's a bit like in the old message-object vs object-message
    discussion. Both have pros and cons.

    The behavior makes some code a bit more "readable", e.g.,
    when using TO-arrays ( stockprice here):

    55e TO (( 5 )) stockprice

    However, it could be that the index calculation uses
    VALUEs too (very visibly here),

    5 value ix
    55 (( ix )) stockprice

    and then it quickly becomes far less pretty. In FysForth
    they let (( and )) stack/unstack the VALUE selector message.

    In iForth I have never used (( )) or value arrays and never
    missed them.

    -marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Wed Jul 31 10:31:29 2024
    1 value fv
    222 to cr
    fv

    looks weird but probably works with a non-parsing TO i.e.
    store 222 in fv.

    TO stores an id as a message in a global variable which is
    queried and reset by the next value.

    It's a bit like in the old message-object vs object-message
    discussion. Both have pros and cons.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From mhx@21:1/5 to mhx on Wed Jul 31 13:18:41 2024
    On Wed, 31 Jul 2024 13:15:55 +0000, mhx wrote:

    Too many errors :-(

    5 value ix
    55 (( ix )) stockprice

    should be

    5 value ix
    55e TO (( ix )) stockprice

    -marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Thu Aug 1 12:35:32 2024
    minforth@gmx.net (minforth) writes:
    the runtime action of an xVALUE (even when
    compiled)
    involves to walk a type-specific operator chain (a large CASE construct
    in VFX).

    Instead of a CASE, an alternative is to have a table that is indexted
    by the number of the operator. This is used in Gforth. And AFAICS,
    we managed to get rid of the OPERATOR variable. Instead, the operator
    passes the index on the stack to "(to)" or "(to),", which does the
    table lookup and then runs ("(to)") or compiles ("(to),") the
    appropriate method.

    Gforth uses a parsing approach, which allows passing the index on the
    stack instead of through a variable and provides better error
    checking. E.g., for TO the implementation is:

    : int-to ( "name" x -- ) \ gforth-internal
    \g Interpretation semantics of \code{to}.
    record-name 0 (') (to) ;

    : comp-to ( compilation "name" -- ; run-time x -- ) \ gforth-internal
    \g Compilation semantics of \code{to}.
    record-name 0 (') (to), ; immediate restrict

    ' int-to ' comp-to interpret/compile: TO ( value "name" -- ) \ core-ext
    \g changes the value of @var{name} to @var{value}

    Here 0 is the index into the table. RECORD-NAME just records
    meta-information for Gforth's development environment. For (TO) the
    help text is:

    '(to)' ( val operation xt -- ) gforth-1.0 "paren-to"
    xt is of a value like word name. Stores val 'to' name. operation
    selects between 'to' (0), '+to' (1), 'addr' (2), 'action-of' (3) and
    'is' (4).

    (TO) is a method of the word specified by xt, so different words have
    different implementations, but they are basically to look up the
    appropriate table entry end execute it. E.g., for a word defined with
    VALUE, the table entry with index 0 (i.e., TO) of the word is the xt
    of "!", and (TO) performs

    ( val xt ) >body !

    The thing about "(TO)," means that when compiling this stuff, the
    whole table lookup is performed at compile time, and what is compiled
    is equivalent to

    ( val ) [ xt >body ] literal !

    - 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 2024: https://euro.theforth.net

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Thu Aug 1 13:35:54 2024
    minforth@gmx.net (minforth) writes:
    On Sat, 27 Jul 2024 15:35:55 +0000, Anton Ertl wrote:
    minforth@gmx.net (minforth) writes:
    [[: ;]] define closures, but unlike gforth's more flexible
    flat closures, they capture simply all upvalues (here local a).

    Gforth's flag closures can be considered lower-level (they come out of
    implementation ideas in the Scheme community) and are easier to
    implement, but emulating closures that capture outer locals is more
    cumbersome.

    In MF36 quotations already have read/write access to the locals of the
    parent function. So closures became a simple by-product through
    capturing
    the parent's locals stack and injecting it at startup during closure
    runtime. When the closure finishes, the captured stack is simply
    updated.

    I don't know if this would be good enough for a Lisp/Scheme programmer,
    but it works for my needs. And look Ma, no garbage collection. :-)

    But then, with

    | : INIT { a }
    | [[: a ;]] \ read counter
    | [[: 1 +to a ;]] ; \ increment counter
    | DEFER count IS count
    | DEFER read IS read
    | 5 INIT
    | COUNT COUNT READ -> should give 7

    is A not gone after INIT is finished, and COUNT and READ will do ... interesting things?

    One other aspect is that in Gforth's closures the
    programmer decides whether closures are allocated in the dictionary,
    on the locals stack, on the heap, or elsewhere.

    In MF36 it is only the locals stack. AFAIU in other programming
    languagues there are closures where the parent function can be seen as >constructor. IOW call the parent function twice and you get different >xts/addresses. From this perspective MF36 closures would perhaps only
    qualify as 'half-closures'.

    Your description sounds like MF36 can do downwards funargs (like
    Pascal), but not upward funargs.

    This raises the question of why you want to use closures for this
    task. Why not use one of the object-oriented Forth packages, some of
    which support value-flavoured fields (Mini-OOF2 among them AFAIK).

    MF36 VALUEs and LOCALs are already built with method VTs. TO et al
    just call the method in the correct method slot.

    Sure, but the point here is how to define an INIT that initializes
    some data, and then you can call COUNT and READ on it, possibly with
    different INITs for different types. And you don't want to give names
    to all the things that are produced by INIT. That sounds like a job
    for an object-oriented package.

    - 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 2024: https://euro.theforth.net

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Thu Aug 1 16:35:26 2024
    On Thu, 1 Aug 2024 13:35:54 +0000, Anton Ertl wrote:

    I don't know if this would be good enough for a Lisp/Scheme programmer,
    but it works for my needs. And look Ma, no garbage collection. :-)

    But then, with

    | : INIT { a }
    | [[: a ;]] \ read counter
    | [[: 1 +to a ;]] ; \ increment counter
    | DEFER count IS count
    | DEFER read IS read
    | 5 INIT
    | COUNT COUNT READ -> should give 7

    is A not gone after INIT is finished, and COUNT and READ will do ... interesting things?

    Of course not. When the compiler encounters the first [[: it lays
    down code to capture the contents INIT's locals stack (environment)
    when INIT is executed. All subsequent [[: "closures" are just
    quotations which inject the captured stack segment into their own
    locals stack, and update the captured environment when finished and
    before their own locals stack is discarded.

    In this way these "closure-flavoured quotations" see an updated A
    i.e. shared upvalue, whenever one of them is called again. Execute
    INIT a second time and the previously captured environment will be
    overwritten.

    The code is only a few lines long. I don't know if Pascal does
    similar things, or if funarg problems are a show-stopper. And of
    course this simple scheme is not OOP, but when it comes quasi for
    free and is handy enough for small applications..

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