From Newsgroup: comp.lang.forth
Jensen's device <
https://en.wikipedia.org/wiki/Jensen%27s_device>
demonstrates capabilities of Algol 60's call-by-name that later
programming languages have provided through other means. <
https://rosettacode.org/wiki/Jensen's_Device> shows solutions for
many other programming languages, including Forth.
The rosettacode task includes just one call to sum:
sum (i, 1, 100, 1/i)
This makes it easy to implement something that is not as general as
the Algol 60 version. This is shown particularly by the first Forth
version <
https://rosettacode.org/wiki/Jensen's_Device#Forth>:
: sum 0 s>f 1+ swap ?do i over execute f+ loop drop ;
:noname s>f 1 s>f fswap f/ ; 1 100 sum f.
It passes i on the stack. How would one use this SUM for the
following call given by wikipedia:
Sum(i, l, m, Sum(j, l, n, A[i,j]))
The second Forth version is as follows:
fvariable ii \ i is a Forth word that we need
: sum ( xt1 lo hi xt2 -- r )
0e swap 1+ rot ?do ( addr xt r1 )
i s>f over execute f! dup execute f+
loop 2drop ;
' ii 1 100 :noname 1e ii f@ f/ ; sum f.
This is in many respects very close to the Algol version. II is a
global variable, but given the way that the first parameter of SUM is
used (just as a memory location for storing and accessing the loop
index), that does not pose problems; the use of a global variable
eliminates the need to deal with scoping.
II is also an FP variable (the Algol 60 version uses integer), but one
could just as well use a cell instead:
variable ii \ i is a Forth word that we need
: sum ( xt1 lo hi xt2 -- r )
0e swap 1+ rot ?do ( r1 xt1 xt2 )
i 2 pick execute ! dup execute f+
loop 2drop ;
' ii 1 100 :noname 1e ii @ s>f f/ ; sum f.
I have written a version using Gforth (development) features to allow
using local storage (where the memory for II/I1 is reclaimed at the
end of MAIN):
: sum {: xt: i1 lo hi xt: term -- r :}
\ stack effects: i1 ( -- addr ); term ( -- r1 )
0e hi 1+ lo +do
i i1 ! term f+
loop ;
: main ( -- )
0 {: w^ i1 :} i1 [n:l ;] 1 100 i1 [n:l @ s>f 1/f ;] sum f. ;
main
The SUM code does exactly the same things as in the second Forth
version above, but the implementation uses locals, including
defer-flavoured locals I1 and TERM (indicated by the XT: before the
local definition). I think the resulting code is easier to follow,
but I am sure others will disagree.
The more insteresting part is in MAIN. Here we create a
variable-flavoured local I1 (alternatively, one could create a
cell-sized buffer) as a replacement for the global II. To bind I1 in
the xts passed to SUM, this code uses pure-stack closures: The stack
effects involved are:
( addr ) [n:l ( addr ) ;] ( xt < -- addr > )
( addr ) [n:l ( addr ) @ s>f 1/f ( r ) ;] ( xt < -- r > )
So these pure-stack closures take the I1 address from the data stack
at closure construction time and push it on the data stack at closure
execution time. The N in [N:L indicates that one cell is involved,
the L indicates that the memory for the closure has the lifetime of
locals (in this case until the end of MAIN).
The C entry points out that macros are close to call-by-name
semantics. Let's see how that works out in Gforth:
: ]sum ( compile-time: ) {: xt: i1 xt: term -- :}
\ run-time: ( lo hi -- r )
] ]] 0e 1+ swap +do
i i1 ! term f+
loop [[ ; immediate
variable i1
: term1 i1 @ s>f 1/f ;
: main2 ( -- )
[ ' i1 ] 1 100 [ ' term1 ]sum f. ;
main2 cr
Here main2 is decompiled as:
: main2 #1 #100
0.00000000000000E0 1+ swap
+do i i1 ! term1 f+
loop
f. ; ok
This variant reverts to using a global variable I1, because the macro
is expanded at compile-time and dealing with run-time locals in macros
is an unsolved problem in Gforth. It uses a named definition TERM1
rather than a quotation for better readability of the decompiler
output. The code of the macro is remarkably similar to the code of
the Gforth SUM above, thanks to ]] ... [[, and also the way
POSTPONE/]] treats defer-flavoured locals (it COMPILE,s them).
- 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
--- Synchronet 3.20a-Linux NewsLink 1.114