Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Interpretation semantics of EXIT

249 views
Skip to first unread message

none albert

unread,
Apr 17, 2022, 6:57:14 AM4/17/22
to
Newsgroups: comp.lang.forth
Subject: Interpretation semantics for EXIT
X-Newsreader: trn 4.0-test77 (Sep 1, 2010)

For ages now I have used the documented property of ciforth
that EXIT in interpretation mode exits INTERPRET.
A simple example would be
S" 1 2 EXIT 3 4" EVALUATE
that leaves `` 1 2 '' on the stack.
EVALUATE takes care that the original input source is
restored, and so does LOAD and INCLUDE.
I use this extensively in blocks; a block intended
for 32 bits MS-Windows is abandoned in 64 bits, or
Linux.

I am working on a lisp interpreter, inspired by
(Chuck Moore's?) BASIC interpreter. In lisp nested data
structures abound even more.
(+ 1 2 (* 2 3 4))
is a typical lisp phrase.
The basic idea is to start INTERPRET in `` ( '' with a special
CONTEXT and start a **new INSTANCE** of INTERPRET with
each next `` ( ''. However in this case you don't
save and restore the input source, but leave it as is.
Now define
' EXIT ALIAS )
and you can build a partial datastructure starting
with the corresponding `` ( '' as soon as the INTERPRET
has finished.

A more mainstream application is in REQUIRE.
A source is included and it is discovered that the facility
has already been loaded, so the remainder of the source is
discarded.
A word that abandons the current input source is long overdue,
and I'm not quite sure that EXIT is the right name.

Groetjes Albert
--
"in our communism country Viet Nam, people are forced to be
alive and in the western country like US, people are free to
die from Covid 19 lol" duc ha
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Anton Ertl

unread,
Apr 18, 2022, 12:12:09 PM4/18/22
to
albert@cherry.(none) (albert) writes:
>A more mainstream application is in REQUIRE.
>A source is included and it is discovered that the facility
>has already been loaded, so the remainder of the source is
>discarded.

In Gforth we implement REQUIRE by checking first if the file has
already been loaded. IIRC the reference implementation of REQUIRED
and REQUIRE also does this. No need to discard the rest of the source
for this.

>A word that abandons the current input source is long overdue,

It seems that we have not needed such a word in Gforth, because we
don't have one. Others have such a word, so there seems to be some
need for it.

>and I'm not quite sure that EXIT is the right name.

Probably not, because EXIT has no interpretation semantics. The EXIT
and INTERPRET implementations on some systems may work to achieve the
effects you have in mind (although I doubt that it works for INCLUDED
and friends on many systems, because INCLUDED is one level out from
the processing that EVALUATE and LOAD perform).

As for the interpretation semantics of EXIT, we tried hard to give
Gforth's EXIT it's execution semantics as interpretation semantics;
this work is described in [ertl15]. However, in addition to the
complications described there, we found that we still had not covered
all the bases, requiring even more complications, so we reverted the
changes to EXIT, and in the development version EXIT is an immediate
word that *compiles* the execution semantics of EXIT when used
interpretively.

@InProceedings{ertl15,
author = {M. Anton Ertl and Bernd Paysan},
title = {From \texttt{exit} to \texttt{set-does>} --- A Story of {Gforth} Re-Implementation},
crossref = {euroforth15},
pages = {41--47},
url = {http://www.euroforth.org/ef15/papers/ertl.pdf},
url-slides = {http://www.euroforth.org/ef15/papers/ertl-slides.pdf},
OPTnote = {not refereed},
abstract = {We changed \code{exit} from an immediate to a
non-immediate word; this requires changes in the
de-allocation of locals, which leads to changes in
the implementation of colon definitions, and to
generalizing \code{does>} into \code{set-does>}
which allows the defined word to call arbitrary
execution tokens. The new implementation of locals
cleanup can usually be optimized to similar
performance as the old implementation. The new
implementation of \code{does>} has performance
similar to the old implementation, while using
\code{set-does>} results in speedups in certain
cases.}
}

- 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: http://www.forth200x.org/forth200x.html
EuroForth 2021: https://euro.theforth.net/2021

none albert

unread,
Apr 19, 2022, 4:34:58 AM4/19/22
to
In article <2022Apr1...@mips.complang.tuwien.ac.at>,
Anton Ertl <an...@mips.complang.tuwien.ac.at> wrote:
>albert@cherry.(none) (albert) writes:
>>A more mainstream application is in REQUIRE.
>>A source is included and it is discovered that the facility
>>has already been loaded, so the remainder of the source is
>>discarded.
>
>In Gforth we implement REQUIRE by checking first if the file has
>already been loaded. IIRC the reference implementation of REQUIRED
>and REQUIRE also does this. No need to discard the rest of the source
>for this.

This is one implementation possibility. What I described is common
in c.
>
>>A word that abandons the current input source is long overdue,
>
>It seems that we have not needed such a word in Gforth, because we
>don't have one. Others have such a word, so there seems to be some
>need for it.
I hope I made the case that it is useful.
>
>>and I'm not quite sure that EXIT is the right name.
>
>Probably not, because EXIT has no interpretation semantics. The EXIT
>and INTERPRET implementations on some systems may work to achieve the
>effects you have in mind (although I doubt that it works for INCLUDED
>and friends on many systems, because INCLUDED is one level out from
>the processing that EVALUATE and LOAD perform).

A standard program according ISO can not use EXIT in interpretation
state. That is not a dogma, but a design decision.
In my implementation EXIT performs a return, i.e. pops a
continuation address for the program counter, and jumps there.
If I remove the ?EXEC (that serves to forbid EXIT in compilation
state), suddenly it performs the useful function of ending INTERPRET.
Making Forth better by removing a line, isn't that the tao of
Forth?

>
>As for the interpretation semantics of EXIT, we tried hard to give
>Gforth's EXIT it's execution semantics as interpretation semantics;
>this work is described in [ertl15]. However, in addition to the
>complications described there, we found that we still had not covered
>all the bases, requiring even more complications, so we reverted the
>changes to EXIT, and in the development version EXIT is an immediate
>word that *compiles* the execution semantics of EXIT when used
>interpretively.

I promote the case that EXIT in interpretation mode exits
INTERPRET. Much to my surprise INTERPRET is not ISO standard.
The description would be:
repeat getting words from the input and perform compilation and execution
according to STATE until the input is exhausted.
A quick check reveals that most system has it:
gforth mpeforth swiftforth.
I wonder whether it WORD and FIND could make it into the standard
and INTERPRET doesn't.
(WORD and FIND hints at implementation internal words, that
probably are not used in modern implementation.
INTERPRET OTOH is far from outdated.)

>
>@InProceedings{ertl15,
> author = {M. Anton Ertl and Bernd Paysan},
> title = {From \texttt{exit} to \texttt{set-does>} --- A Story
>of {Gforth} Re-Implementation},
> crossref = {euroforth15},
> pages = {41--47},
> url = {http://www.euroforth.org/ef15/papers/ertl.pdf},
> url-slides = {http://www.euroforth.org/ef15/papers/ertl-slides.pdf},
> OPTnote = {not refereed},
> abstract = {We changed \code{exit} from an immediate to a
> non-immediate word; this requires changes in the
> de-allocation of locals, which leads to changes in
> the implementation of colon definitions, and to
> generalizing \code{does>} into \code{set-does>}
> which allows the defined word to call arbitrary
> execution tokens. The new implementation of locals
> cleanup can usually be optimized to similar
> performance as the old implementation. The new
> implementation of \code{does>} has performance
> similar to the old implementation, while using
> \code{set-does>} results in speedups in certain
> cases.}
>}

This confirms my suspicion that LOCAL is the root of all evil ;-)

>
>- anton

minf...@arcor.de

unread,
Apr 19, 2022, 5:24:23 AM4/19/22
to
none albert schrieb am Dienstag, 19. April 2022 um 10:34:58 UTC+2:
> This confirms my suspicion that LOCAL is the root of all evil ;-)
>

You'll love closures then. :-)

dxforth

unread,
Apr 19, 2022, 7:47:55 PM4/19/22
to
But a step too far for 200x quotations - all the disadvantages of
nameless words with little to offer in return :)

NN

unread,
Apr 20, 2022, 7:16:45 AM4/20/22
to
How about calling it BAIL ?

Anton Ertl

unread,
Apr 20, 2022, 11:24:33 AM4/20/22
to
albert@cherry.(none) (albert) writes:
>In article <2022Apr1...@mips.complang.tuwien.ac.at>,
>Anton Ertl <an...@mips.complang.tuwien.ac.at> wrote:
>>albert@cherry.(none) (albert) writes:
>>>A more mainstream application is in REQUIRE.
>>>A source is included and it is discovered that the facility
>>>has already been loaded, so the remainder of the source is
>>>discarded.
>>
>>In Gforth we implement REQUIRE by checking first if the file has
>>already been loaded. IIRC the reference implementation of REQUIRED
>>and REQUIRE also does this. No need to discard the rest of the source
>>for this.
>
>This is one implementation possibility. What I described is common
>in c.

C does not have REQUIRE and instead uses a convention of wrapping the
payload into, e.g.,

#ifndef _STDIO_H
#define _STDIO_H 1
... payload here ...
#endif /* <stdio.h> included. */

and I guess that's what you mean. Despite this convention, C does not
have a way to prematurely end the processing of a file.

In any case, in Forth we have REQUIRED/REQUIRE, so even less need for
such a feature.

>I hope I made the case that it is useful.

You wrote that you find it useful. That does not tell us why anyone
else would find it useful.

>A standard program according ISO can not use EXIT in interpretation
>state.

No. A standard program cannot use the interpretation semantics of
EXIT.

>That is not a dogma, but a design decision.
>In my implementation EXIT performs a return, i.e. pops a
>continuation address for the program counter, and jumps there.
>If I remove the ?EXEC (that serves to forbid EXIT in compilation
>state), suddenly it performs the useful function of ending INTERPRET.
>Making Forth better by removing a line, isn't that the tao of
>Forth?

It's certainly a good idea to remove ?EXEC from EXIT, which would make
your system fail on the following standard code:

: macro1 exit ; immediate
: foo macro1 ;

Better get rid of ?EXEC and ?COMP everywhere. They only check STATE
at the time when the containing words are performed, which is not
appropriate for checking whether the interpretation semantics or the
compilation semantics of a word are performed.

It's fascinating that you used ?EXEC for EXIT. My guess is that this
happened the following way: You saw that EXIT has no interpretation
semantics, so you put in ?COMP. Of course this failed, probably on
the first exection of EXIT. So the ?COMP was obviously wrong. As a
remedy you thought you would put in ?EXEC, and it seemed to work (you
have no immediate words containing EXIT), so you left it in, but it's
just as wrong as ?COMP.

As for having EXIT with interpretation semantics, that's something we
wanted to support, as detailed in the grandparent posting. It's
certainly a good idea to add useful functionality beyond the standard,
but in the benefit was not worth the cost.



>I promote the case that EXIT in interpretation mode exits
>INTERPRET. Much to my surprise INTERPRET is not ISO standard.
>The description would be:
>repeat getting words from the input and perform compilation and execution
>according to STATE until the input is exhausted.
>A quick check reveals that most system has it:
>gforth mpeforth swiftforth.
>I wonder whether it WORD and FIND could make it into the standard
>and INTERPRET doesn't.
>(WORD and FIND hints at implementation internal words, that
>probably are not used in modern implementation.
>INTERPRET OTOH is far from outdated.)

My guess is that WORD and FIND are in Forth-94 because there were no
replacements for them in the standard at the time. For Forth-2012,
there was no replacement for FIND, and WORD is designed to work with
FIND. If you want to propose to make WORD and FIND obsolescent, now
the chances are better, because there is FIND-NAME.

As for INTERPRET, if you want it standardized, make a proposal. You
may want to provide some usage statistics to support your case.

Anton Ertl

unread,
Apr 20, 2022, 11:51:10 AM4/20/22
to
Gforth now also has closures that don't involve locals. E.g., you can
define +FIELD and FCONSTANT as:

: +field ( n "name" -- )
[n:d + ;] alias ;

5 +field x
7 x . \ prints 12

What's happening here is that when +FIELD is running, the closure
consumes one data stack item (n); this is used to build a closure,
with the needed space allocated in the dictionary. The xt of the
closure is left on the stack, and the ALIAS creates a word X with that
xt as its execution semantics. When X is run, n (i.e., 5) is pushed
on the stack, and the "+" adds it to the 7.

So the closure mechanism transfers one data stack item from closure
creation time to closure execution time (and transferring data from
closure creation time to closure execution time is the functional
essence of closures, no locals needed for that). We also have
variants for one double ("[d:d") and one float ("[f:d"), and for
allocating the closure on the locals stack ("[n:l") and on the heap
("[n:h"), and all nine combinations. We don't have variants for more
stack items; you can use the closures that involve locals if you need
that.

minf...@arcor.de

unread,
Apr 20, 2022, 1:14:40 PM4/20/22
to
Thanks for that gforth example. IIUC it puts the burden of "lexical scoping"
on programmer's shoulders. But anyhow: it works, well done.

Second big step would be do this with named locals i.e. put the burden on
compiler's shoulders and let programmers enjoy more readable code. But
that's way beyond the standard, and every Forth system would take (and
have to take) a different implementation route.

Nevertheless I doubt that verbose functional programming makes much
sense in Forth's niche.

none albert

unread,
Apr 21, 2022, 3:57:32 AM4/21/22
to
In article <2022Apr2...@mips.complang.tuwien.ac.at>,
So it is similar to
: CONSTANT , DOES> @ ;
or defined by a class/oo mechanism :
_ class CONSTANT M: @ M; , endclass

What then is de big deal?

>
>- anton

luser droog

unread,
Apr 23, 2022, 9:22:31 AM4/23/22
to
On Tuesday, April 19, 2022 at 3:34:58 AM UTC-5, none albert wrote:
> In article <2022Apr1...@mips.complang.tuwien.ac.at>,
> Anton Ertl <an...@mips.complang.tuwien.ac.at> wrote:
> >Probably not, because EXIT has no interpretation semantics. The EXIT
> >and INTERPRET implementations on some systems may work to achieve the
> >effects you have in mind (although I doubt that it works for INCLUDED
> >and friends on many systems, because INCLUDED is one level out from
> >the processing that EVALUATE and LOAD perform).
> A standard program according ISO can not use EXIT in interpretation
> state. That is not a dogma, but a design decision.
> In my implementation EXIT performs a return, i.e. pops a
> continuation address for the program counter, and jumps there.
> If I remove the ?EXEC (that serves to forbid EXIT in compilation
> state), suddenly it performs the useful function of ending INTERPRET.
> Making Forth better by removing a line, isn't that the tao of
> Forth?
> >

[Applause.]

Anton Ertl

unread,
Apr 23, 2022, 1:12:42 PM4/23/22
to
That's one way to see it. But does the programmer want "lexical
scoping", or something else, with lexical scoping just being the Algol
60 and Scheme way to provide it?

In 2018 I wrote up a draft of a paper on closures for Forth, and I
started with lexical scoping. This might look as follows for the
example above:

: +field ( n "name" -- )
{: n :} [: n + ;] alias ;

To make it easier to implement I required explicitly listing the
locals at the start of the closure that are referenced inside the
closure, and passing the allocation word:

: +field ( n "name" -- )
{: n :}
['] alloth <[{: : n :} n + ;] alias ;

Interestingly, the example I gave in the draft is even longer; I don't
remember what the ":" after the <[{: is intended for. With the
allocation being part of the closure start word and without the extra
":", it could look as follows:

: +field ( n "name" -- )
{: n :} d[{: n :} n + ;] alias ;

Bernd Paysan had the idea to make the implementation even simpler by
passing the data to the closure on the stack, resulting in the syntax
in the published paper (but again, the examples in the paper are more
complex):

: +field ( n "name" -- )
[{: n :}d n + ;] alias ;

This new idea required me to rewrite most of the paper
[ertl&paysan18], in particular, the examples. One thing I noticed is
that many of the examples became simpler (sometimes even simpler than
lexical scoping without the additional requirement), because I no
longer had to put the value into a local in order to pass it to the
closure. I could just leave it on the stack, and inside the closure
it would be a local. This shows IMO that what we want as programmers
is not lexical scoping, but a way to pass data to the closure.

In my EuroForth talk I then outlined that it can be done in a
pure-stack way, without locals. This was intended to be mainly food
for thought, but some time later Bernd Paysan told me that he found
that useful for passing single values, and that he had implemented it,
so one could now write

: +field ( n "name" -- )
[n:d + ;] alias ;

In this case the code is shorter, and once you know what [N:D means, I
think it's easier to understand. For passing multiple values to the
closure, this tends to become cumbersome, so for that you still have
to use the version with locals.

But of course, purists might prefer passing multiple values from the
stack(s) at closure-creation time to closure run-time. That is
certainly possible, and shows that what programmers do with closures
in most cases does not require lexical scoping.

Of course, one might say that those who practice point-free functional
programming have shown that long ago, but that's a different approach.

>Second big step would be do this with named locals i.e. put the burden on
>compiler's shoulders and let programmers enjoy more readable code.

As outlined above, Gforth has had that before it had [N:D, but it's
not lexical scoping.

Last year I wrote a paper [ertl21] about transplanting the ideas I
learned here to an Algol-family language (C).

@InProceedings{ertl&paysan18,
author = {M. Anton Ertl and Bernd Paysan},
title = {Closures --- the {Forth} way},
crossref = {euroforth18},
pages = {17--30},
url = {http://www.complang.tuwien.ac.at/papers/ertl%26paysan.pdf},
url2 = {http://www.euroforth.org/ef18/papers/ertl.pdf},
slides-url = {http://www.euroforth.org/ef18/papers/ertl-slides.pdf},
video = {https://wiki.forth-ev.de/doku.php/events:ef2018:closures},
OPTnote = {refereed},
abstract = {In Forth 200x, a quotation cannot access a local
defined outside it, and therefore cannot be
parameterized in the definition that produces its
execution token. We present Forth closures; they
lift this restriction with minimal implementation
complexity. They are based on passing parameters on
the stack when producing the execution token. The
programmer has to explicitly manage the memory of
the closure. We show a number of usage examples.
We also present the current implementation, which
takes 109~source lines of code (including some extra
features). The programmer can mechanically convert
lexical scoping (accessing a local defined outside)
into code using our closures, by applying assignment
conversion and flat-closure conversion. The result
can do everything one expects from closures,
including passing Knuth's man-or-boy test and living
beyond the end of their enclosing definitions.}
}

@Proceedings{euroforth18,
title = {34th EuroForth Conference},
booktitle = {34th EuroForth Conference},
year = {2018},
key = {EuroForth'18},
url = {http://www.euroforth.org/ef18/papers/proceedings.pdf}
}

@InProceedings{ertl21-kps,
author = {M. Anton Ertl},
crossref = {kps21},
title = {The Essence of Closures---A language design
perspective},
year = {2021},
pages = {26--33},
url = {http://www.complang.tuwien.ac.at/papers/ertl21-kps.pdf},
url-slides = {http://www.complang.tuwien.ac.at/papers/ertl21-kps-slides.pdf},
abstract = {Closures are originally associated with lexically
scoped name binding. However, in the course of
implementing closures in Gforth, it turned out that
the actual function (the essence) of closures is to
communicate data between closure creation and the
closure execution (with the closure call usually
being far from the closure creation). This paper
presents a simple language extension for C:
two-stage parameter passing, implemented with flat
closures; the first stage creates a closure, the
second stage calls it. Nested functions and access
to outer locals are not needed.}
}

@Misc{kps21,
author = {{KPS 2021}},
editor = {Hanus, Michael
and Prott, Kai-Oliver
and von Hanxleden, Reinhard
and Domr{\"o}s, S{\"o}ren},
title = {Tagungsband zum 21. Kolloquium Programmiersprachen und Grundlagen der Programmierung},
booktitle = {Tagungsband zum 21. Kolloquium Programmiersprachen und Grundlagen der Programmierung},
year = {2021},
publisher = {Self-Publishing of Department of Computer Science, Kiel},
address = {Kiel},
volume = {2021/7},
issn = {2194-6639},
doi = {10.21941/kcss/2021/7},
url = {https://macau.uni-kiel.de/receive/macau_mods_00002282},
url2 = {https://doi.org/10.21941/kcss/2021/7},
file = {:https://macau.uni-kiel.de/servlets/MCRFileNodeServlet/macau_derivate_00003380/kcss_2021_07.pdf:PDF},
language = {en}

Anton Ertl

unread,
Apr 23, 2022, 1:41:37 PM4/23/22
to
Yes, I selected an example that Forth programmers may be more familiar
with than the numeric integration example that I used more heavily in
the paper and that has led to complaints that people unfamiliar with
numerical integration cannot follow the example.

So yes, you can implement this +FIELD with DOES>:

: +field ( n "name" -- )
create ,
does> @ + ;

(BTW, the stack effect of all +FIELD examples I gave in this thread is
non-standard, but that should not detract from the point of the
example (at least nobody has complained about it yet).

Here are some differences between closures and CREATE...DOES>:

* A closure is nameless (so the code above uses ALIAS to give it a
name); there is no standard way to make a nameless CREATEd word.

* A closure can be allocated in the dictionary (as done here), on the
heap, on the locals stack, or using a user-supplied allocation word.
With CREEATE..DOES>, you are limited to dictionary allocation (and
its lifetime).

* [N:D takes a cell off the stack at closre creation time, and pushes
it on closure execution time, without the programmer having to
manage the data in between (other than supplying an allocation
method). With CREATE..DOES>, the programmer has to store the data
to the dictionary with "," and get it from there with @.

* [N:D can manage the data in any way it sees fit, and the programmer
knows very little about that. As a result, the data is immutable
for the program, so the closure could be inlined, and the data could
be compiled as a literal (with a very sophisticated compiler). By
contrast, the user can change the data of a CREATE-+FIELD child with
>BODY, so even a very sophisticated implementation has to compile a
fetch of that data.

Whether you consider these differences a big deal, is up to you.

dxforth

unread,
Apr 23, 2022, 9:19:05 PM4/23/22
to
Except ANSForth isn't Forth. It's a federation (or confederation if one
believes it was a choice) of Forth implementers. EXIT in interpret state
is one of the many things it couldn't get agreement upon. Mistaking
ANSForth for a created language is what results in the 'I could have done
it better' complaints. Well yes, almost anyone could have created a better
Forth but that's not what was being offered.

none albert

unread,
Apr 24, 2022, 4:34:09 AM4/24/22
to
If you allow me to use the alternative oo-ish definition of constant
`` _ class CONSTANT M: get @ M; , endclass ''
gives me also a BUILD-CONSTANT that does exactly that,

>
>* A closure can be allocated in the dictionary (as done here), on the
> heap, on the locals stack, or using a user-supplied allocation word.
> With CREEATE..DOES>, you are limited to dictionary allocation (and
> its lifetime).

I prefer to separate those interests. As soon as it is important
to allocate objects to a heap -- and it is, occasionally -- I'm inclined
to add an extra word to an allocate wordset

13 \ Make a freshly created object at `HERE permanent. (o -- o )
14 : >ALLOC DUP >R HERE OVER - DUP ALLOCATE THROW
15 DUP >R SWAP MOVE R> R> DP ! ;

If you wish you can redefine BUILD-CONSTANT :
: BUILD-CONSTANT BUILD-CONSTANT >ALLOC ;

>
>* [N:D takes a cell off the stack at closre creation time, and pushes
> it on closure execution time, without the programmer having to
> manage the data in between (other than supplying an allocation
> method). With CREATE..DOES>, the programmer has to store the data
> to the dictionary with "," and get it from there with @.

I agree that one CREATE..DOES> gets you only so far, so it is
good to think further.

>
>* [N:D can manage the data in any way it sees fit, and the programmer
> knows very little about that. As a result, the data is immutable
> for the program, so the closure could be inlined, and the data could
> be compiled as a literal (with a very sophisticated compiler). By
> contrast, the user can change the data of a CREATE-+FIELD child with
> >BODY, so even a very sophisticated implementation has to compile a
> fetch of that data.

The idea of objects is that you guide the way the user is to
handle CONSTANTS as above . In general I have not supplied
a method a user can change the constant, so that is similar.
`` _ class CONSTANT M: get @ M; , endclass ''

So the class definition of CONSTANTS is superior to using
CREATE in much the same way in this respect.
(Except for getting through a heavy-handed use of a `this' pointer
that is similar to use of >BODY ).

Rules that an optimiser can follow, are in this context maybe
simpler. I don't believe in the complicated things a compiler
can do if she is allowed to assume that the e.g. a c-program does
nothing "undefined". (As do you apparently.)
Early FORTRAN's had the rule that if a variable was identified
in two different ways (aliasing), optimisation wasn't allowed, and
that the programmer has to take care. Those Spartan
attitudes in regard to optimisation is more the Forth way.
I guess that closures introduced in Forth would require a lot
of caveats, that is not worth it, and cramps the Forth way,
as in IMHO the use of LOCAL values, and global VALUEs.

>
>Whether you consider these differences a big deal, is up to you.

For the moment I will prefer simple additions to Forth to
achieve similar goals. I'm working on an optimiser and that
may change my opinion.

>
>- anton

Groetjes.

none albert

unread,
Apr 24, 2022, 4:39:08 AM4/24/22
to
You could not accuse me of not embracing ANSForth. Proposals in this way
serve as incremental improvements of Forth.
Considering ANSForth as dogma, was that something Jeff Fox and Chuck Moore
warned about?

Anton Ertl

unread,
Apr 24, 2022, 1:26:53 PM4/24/22
to
albert@cherry.(none) (albert) writes:
>In article <2022Apr2...@mips.complang.tuwien.ac.at>,
>Anton Ertl <an...@mips.complang.tuwien.ac.at> wrote:
>>Here are some differences between closures and CREATE...DOES>:
>>
>>* A closure is nameless (so the code above uses ALIAS to give it a
>> name); there is no standard way to make a nameless CREATEd word.
>
>If you allow me to use the alternative oo-ish definition of constant
>`` _ class CONSTANT M: get @ M; , endclass ''
>gives me also a BUILD-CONSTANT that does exactly that,

Sure, but then we are not into comparing Gforth's closures with
CREATE...DOES>, but comparing them with your OO extension.

>>* [N:D can manage the data in any way it sees fit, and the programmer
>> knows very little about that. As a result, the data is immutable
>> for the program, so the closure could be inlined, and the data could
>> be compiled as a literal (with a very sophisticated compiler). By
>> contrast, the user can change the data of a CREATE-+FIELD child with
>> >BODY, so even a very sophisticated implementation has to compile a
>> fetch of that data.
...
>Rules that an optimiser can follow, are in this context maybe
>simpler. I don't believe in the complicated things a compiler
>can do if she is allowed to assume that the e.g. a c-program does
>nothing "undefined". (As do you apparently.)

It's unclear what you think that I do apparently, but you can find my
position on undefined behaviour in C in
<http://www.complang.tuwien.ac.at/papers/ertl17kps.pdf>

Concerning Forth, I think we should eliminate many ambiguous
conditions or at least define them in the implementations. But I
think that, e.g., it is not a valuable feature to be able to change
constants in the way that is possible in classic threaded-code
systems:

5 constant x
: foo x ;
6 ' x >body !
x . \ 6 on gforth-0.7.9 gforth-itc-0.7.9 sf-3.11 vfxlin-4.72; crash on iforth
foo . \ 6 on gforth-itc-0.7.9; 5 on gforth-0.7.9 sf-3.11 vfxlin-4.72 iforth

Of course, if you think it if valuable to get 6 from the last line,
you can use gforth-itc or any other threaded-code system, at a certain
cost in performance: about a factor 2-3 for gforth-itc compared to
gforth-fast.

Likewise, I don't think it is a valuable feature to be able to change
the offset of +FIELD after it has been defined, so it is useful to be
able to define it in a way without that feature. If you want that
feature, you can define it with closures:

: +field ( n "name" -- )
here swap , [n:d @ + ;] alias ;

It's more cumbersome, and there is no easy way to find the cell you
would change, but if you find and change that cell, the offset will
change everywhere.

Alternatively, you can implement [N:D in your system in such a way
that you can also change the offset with the other [N:D-based
definitions of +FIELD, just like you can change constants in
gforth-itc.

Concerning the complexity of the compiler, compiling a closure into
another word efficiently is relatively simple: compile the value(s)
passed into the closure as literal(s), then compile a call to the code
inside the closure (possibly inlining the call).

E.g., if I have

: +field ( n "name" -- )
[n:d + ;] alias ;

5 +field x

: foo x @ ;

this could be compiled with relatively little effort as

\ here's a named version of the closure code:
: +field-closure1-code
+ ;

: foo 5 +field-closure1-code @ ;

Compiling that with vfxlin-4.72 gives:

see foo
FOO
( 080C0AC0 8B5B05 ) MOV EBX, [EBX+05]
( 080C0AC3 C3 ) NEXT,
( 4 bytes, 2 instructions )

>>Whether you consider these differences a big deal, is up to you.
>
>For the moment I will prefer simple additions to Forth to
>achieve similar goals.

Implementing [N:D and friends is pretty simple. Currently closures.fs
has 227 lines (including comment lines and empty lines), but includes
a lot of extra functionality.

How big are your simple additions?

none albert

unread,
Apr 24, 2022, 3:07:17 PM4/24/22
to
<SNIP>
>
>Implementing [N:D and friends is pretty simple. Currently closures.fs
>has 227 lines (including comment lines and empty lines), but includes
>a lot of extra functionality.
>
>How big are your simple additions?

ALLOCATE : 3 screens ( >ALLOC sneaked in)
class : 1 screen
FORMAT&EVAL : 1 screen (needed for class)
SWAP-DP : 1 screen (scratch compilation area)
Likewise they contain functionality besides implementing
defective pseudo-closures.

These are compacted source library items. The originals were developed
using files. Reckon some 30 lines for source files per block,
maybe more if REGRESS (test lines) are present.

To have an idea of code I use woc (words of code),
the count doesn't differ between file source and block source.
The woc-count is 615 in total. The loc-count is not saying much
depending how generous one is regarding blank lines, or
the presence of the GPL2 copyright in full.
The woc count is also not sensitive to a horizontal or
vertical layout.

----------------------
#!/bin/sh

cat $1 |\
sed -e 's/\\ .*//' |\
sed -e 's/( [^)]*)//g' |\
sed -e '/\<DOC\>/,/\<ENDDOC\>/d'|\
sed -e 's/\\D .*//' |\
wc -w

exit
----------------------


>
>- anton
>--
>M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html

Groetjes Albert

none albert

unread,
Apr 24, 2022, 4:06:09 PM4/24/22
to
In article <2022Apr2...@mips.complang.tuwien.ac.at>,
Anton Ertl <an...@mips.complang.tuwien.ac.at> wrote:
>albert@cherry.(none) (albert) writes:
<SNIP>
>Better get rid of ?EXEC and ?COMP everywhere. They only check STATE
>at the time when the containing words are performed, which is not
>appropriate for checking whether the interpretation semantics or the
>compilation semantics of a word are performed.
>
>It's fascinating that you used ?EXEC for EXIT. My guess is that this
>happened the following way: You saw that EXIT has no interpretation
>semantics, so you put in ?COMP. Of course this failed, probably on
>the first exection of EXIT. So the ?COMP was obviously wrong. As a
>remedy you thought you would put in ?EXEC, and it seemed to work (you
>have no immediate words containing EXIT), so you left it in, but it's
>just as wrong as ?COMP.

I introduced ?EXEC in my story to get a point across.
I don't actually use ?EXEC. If you look at my example of INTERPRET,
you would realize that I don't check compilation mode in EXIT.
In my implementation EXIT is a code word that pops the high level
interpreter pointer from the return stack, then goes next.
It is rigorously equivalent to the return instruction native
code forth's use.

?EXEC is available as a loadable extension if you want to go traditional:
65( -traditional- ?LOADING ?EXEC TRIAD ABORT" ) \ AvdH C2feb22

>
>As for having EXIT with interpretation semantics, that's something we
>wanted to support, as detailed in the grandparent posting. It's
>certainly a good idea to add useful functionality beyond the standard,
>but in the benefit was not worth the cost.

Good to hear that.

>
>As for INTERPRET, if you want it standardized, make a proposal. You
>may want to provide some usage statistics to support your case.

INTERPRET blends in in the ciforth model. It is not trivial to
made it fit to ISO, as this is not a model, and a lot of details
can go wrong. I will reflect upon the matter.
If you build a Forth from scratch, you are forced to implement WORD
with special case BL WORD, but you may not want use that internally. I
don't want to add another such word.

>
>- anton
>--

dxforth

unread,
Apr 24, 2022, 10:16:53 PM4/24/22
to
EXIT was a regular word in Forth-79/83 until someone decided they could
'improve' on it.


Anton Ertl

unread,
Apr 25, 2022, 2:04:25 AM4/25/22
to
albert@cherry.(none) (albert) writes:
>To have an idea of code I use woc (words of code),
>the count doesn't differ between file source and block source.
>The woc-count is 615 in total. The loc-count is not saying much
>depending how generous one is regarding blank lines, or
>the presence of the GPL2 copyright in full.
>The woc count is also not sensitive to a horizontal or
>vertical layout.
>
>----------------------
>#!/bin/sh
>
>cat $1 |\
> sed -e 's/\\ .*//' |\
> sed -e 's/( [^)]*)//g' |\
> sed -e '/\<DOC\>/,/\<ENDDOC\>/d'|\
> sed -e 's/\\D .*//' |\
> wc -w
>
>exit
>----------------------

cat closures.fs |
sed -e 's/\\ .*//' |
sed -e 's/( [^)]*)//g' |
sed -e 's/\\G .*//'|
sed -e 's/\\D .*//'|
head -209|
wc -w
622

The "head -209" is there to avoid counting the man-or-boy-test that's
there as a test (normally inactive).

luser droog

unread,
Apr 28, 2022, 12:28:53 AM4/28/22
to
I don't know enough about the whole story to weigh in there,
just saying that Albert made a compelling point. So compelling
that the rest of the story kinda reads as just noise to me.
If I can figure out how to use it, my personal forth will do it this way.
And that's all I'm likely to use anyway.

Until I stumble across something better.

dxforth

unread,
Apr 28, 2022, 2:46:26 AM4/28/22
to
For a while I had an EXIT with a ?COMP. To stop interpretation of a screen
I defined:

: \\ source >in ! drop ; immediate

For text files it was extended to:

-? : \\
tf? if
begin tfill 0= until
end postpone \\ ; immediate

When EXIT reverted to a regular word I saw little point changing them. What
was once a neat trick back in the Fig-Forth days had lost its shine.

Hans Bezemer

unread,
Apr 28, 2022, 8:46:12 AM4/28/22
to
On Monday, April 25, 2022 at 4:16:53 AM UTC+2, dxforth wrote:
> EXIT was a regular word in Forth-79/83 until someone decided they could
> 'improve' on it.

I agree. In 4tH, having Forth-79 roots, EXIT is still a very simple word: get TORS,
put it in the instruction pointer and continue (Ok, some checks are made - agreed).

It's not that much different from a RET instruction in Z80 - and why should it? It's
just there as a counterpart for a CALL.

Of course, if you add several other services (like taking care of locals) it will most
probably become a bit beefier. That's one of the reasons why I don't like locals in
the first place.

Of course I love them in C - but C is not Forth.

And those who have a history with my ramblings here won't be too surprised to
hear that I don't like over-complicated Forths, which absolutely improve in "ease of
use", but IMHO lose this "simple elegance" quality of Forth I fell in love with.

So dxforth, I concur with you on this point.

Hans Bezemer

none albert

unread,
Apr 28, 2022, 9:13:55 AM4/28/22
to
In article <ce65773c-3b15-40a3...@googlegroups.com>,
Lately I became fond of AUTOLOAD
They CATCH any errors from the interpreting a line.
If the error is 12 of 10 (undefined) , the name is WANTED through the
library. If it succeeds, compilation goes on as if nothing happens.
WANT AUTOLOAD
SEE LOCATE
Neither of SEE or LOCATE is in the kernel, but it works.

Of course AUTOLOAD is a loadable extension, only available after
WANT AUTOLOAD .

>
>So dxforth, I concur with you on this point.

So do I, but it is possible to make life easier without compromising
the simplicity of the kernel Forth.

>
>Hans Bezemer

Hans Bezemer

unread,
Apr 28, 2022, 11:13:20 AM4/28/22
to
On Thursday, April 28, 2022 at 3:13:55 PM UTC+2, none albert wrote:
> >So dxforth, I concur with you on this point.
> So do I, but it is possible to make life easier without compromising
> the simplicity of the kernel Forth.

True - in 4tH (with it's wildly different architecture) I see the same thing
happening. But due to the radical division between "compilation" and
"interpretation", I see it's not happening in the (inner) interpreter. It's
happening in the compiler - or even the tools on top of the compiler, like
the preprocessor.

The VM hasn't become much smarter at all. In 10 (!) years, that code has
seen only 15 changes - roughly a third of them C syntax related, a third of
them architecture related and the final third of them behavior related (incl.
new functionality).

The compiler has seen about fifty changes in the meanwhile - most them
behavior related.

Translated to the classical Forth architecture - primitives and inner interpreter
have hardly changed.

I dunno how that works out for other Forths - I think it would be an interesting
question, though.

Hans Bezemer

none albert

unread,
Apr 29, 2022, 5:37:31 AM4/29/22
to
In article <8d0072a1-9127-41b4...@googlegroups.com>,
Don't stop looking for simplications. I recently managed the central (FIND)
word from 6 to 3 labels and eliminate a third of the underlying
~MATCH word.
The discotheque principle is useful. Nobody gets in, before somebody gets
out. If you add a word, an other word has to get removed.
This would be a useful principle for laws also.

Hans Bezemer

unread,
Apr 29, 2022, 6:36:19 AM4/29/22
to
On Friday, April 29, 2022 at 11:37:31 AM UTC+2, none albert wrote:
> Don't stop looking for simplications. I recently managed the central (FIND)
> word from 6 to 3 labels and eliminate a third of the underlying
> ~MATCH word.
I recently reprogrammed the inner Accept() word. I was hesitant to touch it
since - although murky - it had worked absolutely fine for three decades.

I must have grown a bit in that time, because I found out how it exactly worked.
And the code is not only much clearer now - it has additional functionality!

> The discotheque principle is useful. Nobody gets in, before somebody gets
> out. If you add a word, an other word has to get removed.
> This would be a useful principle for laws also.
I have this policy that 4tH has 106 reserved tokens. For every one added, one
must disappear. That leaves someone who wants to expand on it a clean
150 ones to play with. Every change in a token bumps up the minor version
number. That's why 3.63.x never came to be.

So yes, I agree on that policy. That's why the compiler itself feeds most of
the functionality changes. E.g. I recently added CASE..ENDCASE, but that's
all handled by the compiler. The (inner) interpreter gets fed the very same tokens
as before.

Hans Bezemer

0 new messages