Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #4541 > unrolled thread

multi-threading in Forth?

Started byMichael L Gassanenko <m_l_g3@yahoo.com>
First post2011-08-02 02:01 -0700
Last post2011-08-03 03:53 -0500
Articles 20 on this page of 75 — 18 participants

Back to article view | Back to comp.lang.forth


Contents

  multi-threading in Forth? Michael L Gassanenko <m_l_g3@yahoo.com> - 2011-08-02 02:01 -0700
    Re: multi-threading in Forth? mhx@iae.nl (Marcel Hendrix) - 2011-08-02 11:22 +0200
      Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-02 15:26 +0200
      Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-04 01:06 +0400
        Re: multi-threading in Forth? mhx@iae.nl (Marcel Hendrix) - 2011-08-04 06:36 +0200
          Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 08:25 +0100
            Re: multi-threading in Forth? coos haak <chforth@hccnet.nl> - 2011-08-04 13:42 +0200
              Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 14:35 +0200
                Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-04 08:30 -0500
                  Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 15:38 +0200
                    Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-04 09:12 -0500
                      Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:50 +0100
                      Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-04 23:52 +0400
                        Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-05 08:50 -0500
                    Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:49 +0100
                      Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 19:23 +0200
                      Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-05 10:57 -0500
                Re: multi-threading in Forth? stephenXXX@mpeforth.com (Stephen Pelc) - 2011-08-04 14:16 +0000
                  Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:51 +0100
                    Re: multi-threading in Forth? stephenXXX@mpeforth.com (Stephen Pelc) - 2011-08-04 17:14 +0000
                    Re: multi-threading in Forth? "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2011-08-06 03:46 -0400
                  Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 19:11 +0200
                    Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-05 08:43 -0500
                      Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-06 01:49 +0400
                        Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-06 03:13 -0500
                          Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-09 04:41 +0400
                            Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-09 11:01 +0400
                            Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-10 09:02 -0500
                              Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-11 01:10 +0400
                        Re: multi-threading in Forth? Ouatu Bogdan <ouatubi@gmail.com> - 2011-08-10 13:22 +0000
                      Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-06 01:12 +0200
                        Re: multi-threading in Forth? coos haak <chforth@hccnet.nl> - 2011-08-06 02:37 +0200
                          Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-06 12:54 +0200
                            Re: multi-threading in Forth? alextangent <blog@rivadpm.com> - 2011-08-06 12:15 +0100
                        Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-05 22:23 -0500
                        Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-06 03:17 -0500
                      Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-05 19:02 -0500
                        Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-06 04:01 -0500
                          return-address manipulation (was: multi-threading in Forth?) anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2011-08-06 16:35 +0000
                            Re: return-address manipulation Gerry Jackson <gerry@jackson9000.fsnet.co.uk> - 2011-08-07 11:45 +0100
                              Re: return-address manipulation Josh Grams <josh@qualdan.com> - 2011-08-08 09:40 +0000
                                Re: return-address manipulation Gerry Jackson <gerry@jackson9000.fsnet.co.uk> - 2011-08-08 12:17 +0100
                                  Re: return-address manipulation Josh Grams <josh@qualdan.com> - 2011-08-08 12:29 +0000
                                    Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-09 03:54 +0400
                              Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-09 02:52 +0400
                                Re: return-address manipulation Gerry Jackson <gerry@jackson9000.fsnet.co.uk> - 2011-08-09 20:54 +0100
                                  Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:00 +0400
                            Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-09 14:49 -0500
                              Re: return-address manipulation anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2011-08-10 09:03 +0000
                                Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-10 05:13 -0500
                              Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:24 +0400
                                Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-11 04:09 -0500
                              Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:36 +0400
                                Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:54 +0400
                                Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-11 04:16 -0500
                          Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-07 01:43 -0500
                        Re: multi-threading in Forth? Spam@ControlQ.com - 2011-08-06 12:36 -0400
                          Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-07 01:13 -0500
                            Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-09 02:35 +0400
                          Re: multi-threading in Forth? Albert van der Horst <albert@spenarnc.xs4all.nl> - 2011-08-07 10:08 +0000
                        Re: multi-threading in Forth? Josh Grams <josh@qualdan.com> - 2011-08-07 13:45 +0000
                    Re: multi-threading in Forth? mhx@iae.nl (Marcel Hendrix) - 2011-08-05 17:17 +0200
                      Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-05 19:23 +0200
                    Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-05 10:53 -0500
                      Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-05 19:18 +0200
                        Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-06 09:59 -0500
                          Re: multi-threading in Forth? Spam@ControlQ.com - 2011-08-06 12:28 -0400
                      Re: multi-threading in Forth? Albert van der Horst <albert@spenarnc.xs4all.nl> - 2011-08-06 11:29 +0000
              Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:47 +0100
                Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-04 13:54 -0500
                Re: multi-threading in Forth? coos haak <chforth@hccnet.nl> - 2011-08-04 22:02 +0200
    Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-02 08:23 -0500
      Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-02 15:30 +0200
        Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-02 08:53 -0500
          Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-03 03:53 -0500

Page 2 of 4 — ← Prev page 1 [2] 3 4  Next page →


#4615

From"Rod Pemberton" <do_not_have@noavailemail.cmm>
Date2011-08-06 03:46 -0400
Message-ID<j1irhq$gd5$1@speranza.aioe.org>
In reply to#4583
"Mark Wills" <markrobertwills@yahoo.co.uk> wrote in message
news:j1ef6s$n41$4@dont-email.me...
>
> Perhaps the return stack should be
> obsoleted?
>

If the return stack is obsoleted, how do you intend to ...

a) implement a Forth for a threaded interpreter without it?  E.g., a
threaded interpreter saves and restores interpreter addresses using the
return stack via ENTER/DOCOL and EXIT/NEXT.  Historically, some Forth words
start with R> due to threaded interpreters, e.g.:

  LIT BRANCH COMPILE DOES RP!

b) implement a temporary register without it?  An 'A' register ala
MachineForth?  E.g., a temporary may be needed for SWAP or for words using
>R and R> .

c) resolve the fact that parameter stack items must be reorganized and extra
space is required to do that?

d) keep control flow addresses off of the parameter stack?  I.e., a single
stack is commonly used in C, but not required.  This leads to many of the
problems C has with various exploits.  This would be the same as merging the
return stack and parameter/data stack of Forth into a single stack.


Rod Pemberton


[toc] | [prev] | [next] | [standalone]


#4585

FromZbiggy <zbigniew2011REMOVE@gmail.REMOVE.com>
Date2011-08-04 19:11 +0200
Message-ID<slrnj3ls85.27m.zbigniew2011REMOVE@Tichy.myhome.org>
In reply to#4578
In comp.lang.forth, Stephen Pelc wrote:

> In practice, embedded Forths using 8051s may keep return addresses on
> a third stack. However, what you see more often is that a return
> address may be larger than a cell size (DSP, PIC ...) or may not be
> formatted as you expect (e.g. Cortex-M has bit 0 always set).
>
> Hence, there are occasion when you *need* to isolate the return
> address words because they are not just R@ and friends. Yes, this
> sort of problem is seen regularly.

Of course, it's better to create Forth compiler with "strange return stack",
than to not make it at all. But I understand the described situation rather
as a rationale, why ANS-standard doesn't impose the strict rules about stack:
"...because sometimes can be not possible to fulfil such requirement" - than
as a statement: "actually, return stack can be implemented in any way". Yes:
formally it can - it isn't forbidden by law, therefore no Forth creator will
be punished - but the question is: should it really be treated so blithely?

Nevertheless I'm somewhat amazed, that it is done in situations, when there
can be implemented "proper" return stack. It can make more problems, than
e.g. changing the word name from "<MOVE" to "MOVE>" - because it is the
violation of quite basic rule. What is return stack, is described in every
Forth primer. And then we can see, that the rule works "sometimes".

I think, that "rule" doesn't necessarily mean formal requirement of any
standard committee - it can be just "traditional solution", present and known
since 30 years, if it still does its work with no problems. But of course it
can be changed, when it will mean some obvious profits for both compiler
creator and user, for example: Andy Valencia, ForthOS creator, proposed to
use larger blocks - 4096 instead of "traditional" 1024 bytes - and it does
make sense, because meanwhile the HDD vendors changed the size of basic
sector unit to... 4096 bytes (from 512). So this is an example, how the
change of standard can follow "the life". Using blocks of the same size as
HDD sector means better performance (of course, if one's willing to use
blocks at all).

So if I'm trying to use a compiler written for some "exotic" hardware, yes:
I can expect the presence of some non-standard solution. But why in the case
of compiler written for most popular hardware? There surely is some
rationale behind it, although IMHO if such change won't give any tremendous
profits (much greater efficiency?), perhaps it's better to _not_ introduce
it, because most programmers will expect to find the things working "old way".


Well, of course I don't have so much Forth-programming experience as
everyone of you here (if my exercises can be taken as any "experience" at
all), but this is as I see it.
-- 
...but I'm ready to learn ('bout) of the power of Forth

[toc] | [prev] | [next] | [standalone]


#4595

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2011-08-05 08:43 -0500
Message-ID<b5SdnSwRyaUTbqbTnZ2dnUVZ8sidnZ2d@supernews.com>
In reply to#4585
Zbiggy <zbigniew2011REMOVE@gmail.remove.com> wrote:
> In comp.lang.forth, Stephen Pelc wrote:
> 
>> In practice, embedded Forths using 8051s may keep return addresses on
>> a third stack. However, what you see more often is that a return
>> address may be larger than a cell size (DSP, PIC ...) or may not be
>> formatted as you expect (e.g. Cortex-M has bit 0 always set).
>>
>> Hence, there are occasion when you *need* to isolate the return
>> address words because they are not just R@ and friends. Yes, this
>> sort of problem is seen regularly.
> 
> Of course, it's better to create Forth compiler with "strange return
> stack", than to not make it at all. But I understand the described
> situation rather as a rationale, why ANS-standard doesn't impose the
> strict rules about stack: "...because sometimes can be not possible
> to fulfil such requirement" - than as a statement: "actually, return
> stack can be implemented in any way". Yes: formally it can - it
> isn't forbidden by law, therefore no Forth creator will be punished
> - but the question is: should it really be treated so blithely?

I think so.  There is the matter of whether, say, return stack
manipulation is more useful than tail call optimization.  I'd choose
tail call optimization every time, but IME code that (ab)uses the
return stack for control flow is hard to understand and maintain.
Whatever the problem, there's almost always a better way to do it than
fiddling with the R-stack.

> Andy Valencia, ForthOS creator, proposed to use larger blocks - 4096
> instead of "traditional" 1024 bytes - and it does make sense,
> because meanwhile the HDD vendors changed the size of basic sector
> unit to... 4096 bytes (from 512).

Yes, it makes very good sense to do this.  However, given a 4096-byte
block buffer, you can easily get a compatible BLOCK with

: block ( n - a)   4 /mod big-block  swap 1024 * + ;

So, there is no need for application code to be affected by the use of
big blocks.

Andrew.

[toc] | [prev] | [next] | [standalone]


#4607

Frommlg3 <m_l_g3@yahoo.com>
Date2011-08-06 01:49 +0400
Message-ID<j1hofk$g8o$1@dont-email.me>
In reply to#4595
Andrew Haley wrote:
 > Whatever the problem, there's almost always a better way to do it than
 > fiddling with the R-stack.

Fascinating. A person who apparently has no problems with
the use of data stack, discourages the use of return stack,
although both lead to equally untraditional, from an average
specialist's viewpoint, code, and both are backed by
solid mathematical background.

Probably, the word "fiddling" attempts to characterize it
as something illogical. It's not. But to demonstrate that,
I will introduce a new notation.

== means functional equivalence (*)

[footnote]
(*) that is, we assume that the code is not accessed as data (**).
(**) In fact, some elements of code, such as branch
destinations, _are_ accessed as data, but they are not
executed as code.
[/footnote]

For FOO defined as

: foo bar ;

we write:

foo == nest[ bar exit ]

analogously, for

: baz foo ;

we have

baz == nest[ foo exit ] == nest[ nest[ bar exit ] exit ]

The next thing to realize is that a call is just
placement of an address onto the return stack.

r[ qux ] stands for a code fragment that places the address of the code 
fragment qux onto the return stack.

CREATE quux ] qux exit [

quux >R  ==  r[ qux exit ]

And now, the most important property:

nest[ x ] y ... == r[ y ... ] x

The ellipsis above shows what happens with the code that follows y. The 
ellipsis at the end of an expression may be omitted, for example, we may 
rewrite the above as:

nest[ x ] y == r[ y ... ] x

or even

nest[ x ] == r[ ... ] x

Now,

foo == nest[ bar exit ] == r[ ... ] bar exit

(
Note that
    exit x == exit y == exit
because exit does not pass control to the code that follows it.

Looking at
   nest[ x ] == r[ ... ] x
you might wonder what about the code that follows x. The answer
is: in practice x usually ends with exit, so whatever follows
that exit is irrelevant.
)

Analogously, for baz defined above:

baz == nest[ foo exit ] == nest[ nest[ bar exit ] exit ]
== r[ ... ] r[ exit ] bar exit

Let us consider a more complicated example.
[example1]
: foo ." foo " ;
: qux ." qux " ;
CREATE quux ] qux exit [
: test quux >R foo ;

test == nest[ quux >R foo exit ] == nest[ r[ qux exit ] foo exit ]

It is evident that for our definition of foo
   r[ anything ] foo == foo r[ anything ]
because foo defined above does not depend on and does not
affect the return stack state. This is a very important
property, it is written as foo:R.
(You may notice that I resort to common sense in the middle
of formal transformations. I do not try to try to prove
what we all already know is true, because it is not
important how we prove that. This knowledge is one level
of abstraction below our maths.)

So, continuing the chain,

nest[ r[ qux exit ] foo exit ]
== nest[ foo r[ qux exit ] exit ]
== nest[ foo qux exit ]

because

r[ x ] exit == x

(This is an axiom, and it means that exit transfers control
to the code whose address is taken off the return stack.)

So, test will print "foo qux ". Let's try it in Gforth:

: foo ." foo " ;  ok
: qux ." qux " ;  ok
CREATE quux ] qux exit [  ok
: test quux >r foo ;  ok
   ok
test foo qux  ok
[/example1]


[example2]
And what about
: co r> r> swap >r >r ;
?

: x a co b ;
: y c y d ;

y == nest[ c nest[ a nest[ r> r> swap >r >r exit ] b exit] d exit ]
== r[ ... ] c r[ d exit ] a r[ b exit ] r> r> swap >r >r exit
== r[ ... ] c r[ b exit ] a r[ d exit ] exit
== r[ ... ] c r[ b exit ] a d exit

it would be tempting to continue the transformation, replacing
[ r[ b exit ] a d exit ] with [ a d b exit ] but to do that, we
must know that [a d]:R (the code fragment [a d] does not depend
on or affect the return stack).

(You likely have already figured out that we use brackets
to group two code fragments into one code fragment.)
[/example2]

By the way, the notation for
     r[ something ] R>
is
     '[ something ]
We could use it above, but did not. Of course,
     '[ x ] >R == r[ x ]

And now, some important theorem.
But first, a definition.

Definition
S1( x ) is such a code fragment that
ForAll N: x N == N S1( x )

In other words, it does what x does, but leaves the stack top intact.

Examples:
S1( NOOP ) == NOOP
S1( DROP ) == NIP
S1( DUP ) == OVER SWAP
S1( EXIT ) == DROP EXIT
Indeed, N S1( EXIT ) == EXIT N, and N DROP EXIT == EXIT

Analogously,
Definition
R1( x ) is such a code fragment that
ForAll N: x N >R == N >R R1( x )

that is, R1( x ) acts like x but leaves the return stack top intact.

Examples:
R1( DUP ) == DUP
R1( DROP ) == DROP
R1( RDROP ) == R> RDROP >R
R1( EXIT ) == RDROP EXIT
Indeed, N >R R1( EXIT ) == EXIT N >R, and N >R RDROP EXIT == EXIT

Definition
x:R if x == R1( x )
(x:R means that x does not affect or depend on the return stack)

Examples:
DUP:R
DROP:R


And now, the theorem of call equivalence.

Theorem

x == nest[ R1( x ) exit ]

Proof

nest[ R1( x ) exit ] == r[ ... ] R1( x ) exit
== x r[ ... ] exit == x ... == x

Consequence

For x:R,
x == nest[ x exit ]

(that is, if words in a colon definition do not
read or modify the return stack, a call of the definition
is equivalent to inline substitution of the definition's body).

Are you still with me? ;-)

[toc] | [prev] | [next] | [standalone]


#4616

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2011-08-06 03:13 -0500
Message-ID<t_6dndJzyNwCaqHTnZ2dnUVZ7oudnZ2d@supernews.com>
In reply to#4607
mlg3 <m_l_g3@yahoo.com> wrote:
> Andrew Haley wrote:
> > Whatever the problem, there's almost always a better way to do it than
> > fiddling with the R-stack.
> 
> Fascinating. A person who apparently has no problems with
> the use of data stack, discourages the use of return stack,

I don't discourage the use of return stack.  It's very useful for
saving items from the data stack.  It's not useful for changing
control flow, IME; that's the Forth equivalent of GOTO.

> Probably, the word "fiddling" attempts to characterize it
> as something illogical. It's not. But to demonstrate that,
> I will introduce a new notation.

<formalism elided>

What is the point of this?  It can't be in response to what I wrote,
since I never claimed that fiddling with the return stack was
"illogical".   I did claim that it was, ah, fiddly.  :-)

> (that is, if words in a colon definition do not read or modify the
> return stack, a call of the definition is equivalent to inline
> substitution of the definition's body).

Quite so: this also means that fiddling with the return stack may
destroy this property.  For example,

: bletch   foo bar baz ;

may execute FOO and BAR, but not BAZ, because BAR has the side-effect
of exiting from BLETCH .  Throwing an exception does that too, but
exceptions are constrained in a way that R-stack fiddling is not.  You
can, at least, see the site to which an exception returns.

> Are you still with me? ;-)

Andrew.

[toc] | [prev] | [next] | [standalone]


#4651

Frommlg3 <m_l_g3@yahoo.com>
Date2011-08-09 04:41 +0400
Message-ID<j1pvnb$1n8$1@dont-email.me>
In reply to#4616
Andrew Haley wrote:
> mlg3 <m_l_g3@yahoo.com> wrote:
>> Andrew Haley wrote:

> <formalism elided>

:-(

> 
>> (that is, if words in a colon definition do not read or modify the
>> return stack, a call of the definition is equivalent to inline
>> substitution of the definition's body).
> 
> Quite so: this also means that fiddling with the return stack may
> destroy this property.  For example,

[sigh]

The context was important.

>> Theorem
>> 
>> x == nest[ R1( x ) exit ]

It shows that in the general case there's a transformation.
For example, if we have R> and DROP and want to define RDROP,
the body of the colon definition will contain R1( R> DROP )

: RDROP R> R> DROP >R ;

If we want to extract body from a colon definition (that is,
optimize out the call), it is not possible in some cases.

For example,
: lit& R@ @ AND R> CELL+ >R ;
is better left called because it reads and changes the
interpretation stack (return stack+ip).

And in one particular but important case we get the
consequence, which (consequence) is true but not new.

>> Consequence
>> 
>> For x:R,
>> x == nest[ x exit ]
>> 
>> (that is, if words in a colon definition do not
>> read or modify the return stack, a call of the definition
>> is equivalent to inline substitution of the definition's body).

Evidently, in some particular case we had to get what the
standard says.


 > ... For example,
> 
> : bletch   foo bar baz ;
> 
> may execute FOO and BAR, but not BAZ, because BAR has the side-effect
> of exiting from BLETCH .

If BAR causes an exit from BLETCH, BAR/:R (it's not true that BAR:R)
-- it needs to read the return stack to exit, so the consequence
does not apply.

In fact,

: MAX 2DUP > IF DROP EXIT THEN NIP ;

is a standard definition that may not be inlined because EXIT/:R
(it is not true that EXIT:R)

>  Throwing an exception does that too, but
> exceptions are constrained in a way that R-stack fiddling is not.  You
> can, at least, see the site to which an exception returns.

Let us not mix the flies with the cutlets.

Exceptions, of course, require a special set of axioms, but they
are not relevant now.

*** The result of return address manipulations may be predicted
at compile-time ***

And this is why the formalism is possible.

[toc] | [prev] | [next] | [standalone]


#4653

Frommlg3 <m_l_g3@yahoo.com>
Date2011-08-09 11:01 +0400
Message-ID<j1qlvu$cem$1@dont-email.me>
In reply to#4651
mlg3 wrote:
> Andrew Haley wrote:
>> mlg3 <m_l_g3@yahoo.com> wrote:
>>> Andrew Haley wrote:
> 
>> <formalism elided>
> 
> :-(
> 
>>
>>> (that is, if words in a colon definition do not read or modify the
>>> return stack, a call of the definition is equivalent to inline
>>> substitution of the definition's body).
>>
>> Quite so: this also means that fiddling with the return stack may
>> destroy this property.  For example,
> 
> [sigh]
> 
> The context was important.
> 
>>> Theorem
>>>
>>> x == nest[ R1( x ) exit ]
> 
> It shows that in the general case there's a transformation.
> For example, if we have R> and DROP and want to define RDROP,
> the body of the colon definition will contain R1( R> DROP )
> 
> : RDROP R> R> DROP >R ;
> 
> If we want to extract body from a colon definition (that is,
> optimize out the call), it is not possible in some cases.
> 
> For example,
> : lit& R@ @ AND R> CELL+ >R ;
> is better left called because it reads and changes the
> interpretation stack (return stack+ip).
> 

Sorry, I glitched.
This should read "because it reads and changes the
_return_ stack". (Any word that does not hang/halt
the system reads and/or modifies IP).

> And in one particular but important case we get the
> consequence, which (consequence) is true but not new.
> 
>>> Consequence
>>>
>>> For x:R,
>>> x == nest[ x exit ]
>>>
>>> (that is, if words in a colon definition do not
>>> read or modify the return stack, a call of the definition
>>> is equivalent to inline substitution of the definition's body).
> 
> Evidently, in some particular case we had to get what the
> standard says.
> 
> 
>  > ... For example,
>>
>> : bletch   foo bar baz ;
>>
>> may execute FOO and BAR, but not BAZ, because BAR has the side-effect
>> of exiting from BLETCH .
> 
> If BAR causes an exit from BLETCH, BAR/:R (it's not true that BAR:R)
> -- it needs to read the return stack to exit, so the consequence
> does not apply.
> 
> In fact,
> 
> : MAX 2DUP > IF DROP EXIT THEN NIP ;
> 
> is a standard definition that may not be inlined because EXIT/:R
> (it is not true that EXIT:R)
> 
>>  Throwing an exception does that too, but
>> exceptions are constrained in a way that R-stack fiddling is not.  You
>> can, at least, see the site to which an exception returns.
> 
> Let us not mix the flies with the cutlets.
> 
> Exceptions, of course, require a special set of axioms, but they
> are not relevant now.
> 
> *** The result of return address manipulations may be predicted
> at compile-time ***
> 
> And this is why the formalism is possible.
> 

[toc] | [prev] | [next] | [standalone]


#4689

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2011-08-10 09:02 -0500
Message-ID<uMmdncpD8awdEt_TnZ2dnUVZ8uCdnZ2d@supernews.com>
In reply to#4651
mlg3 <m_l_g3@yahoo.com> wrote:
> Andrew Haley wrote:
>> mlg3 <m_l_g3@yahoo.com> wrote:
>>> Andrew Haley wrote:
> 
>> <formalism elided>
> 
> :-(
> 
>> 
>>> (that is, if words in a colon definition do not read or modify the
>>> return stack, a call of the definition is equivalent to inline
>>> substitution of the definition's body).
>> 
>> Quite so: this also means that fiddling with the return stack may
>> destroy this property.  For example,
> 
> [sigh]
> 
> The context was important.

As far as I could see the formalism told us in a formal way what we
already knew.  Perhaps I missed something, though.  Were there some
uncommon insights?

> > *** The result of return address manipulations may be predicted
> at compile-time ***
> 
> And this is why the formalism is possible.

But what is it for?  I don't understand where this is going.

Andrew.

[toc] | [prev] | [next] | [standalone]


#4703

Frommlg3 <m_l_g3@yahoo.com>
Date2011-08-11 01:10 +0400
Message-ID<j1us4b$api$1@dont-email.me>
In reply to#4689
Andrew Haley wrote:
> mlg3 <m_l_g3@yahoo.com> wrote:
>> Andrew Haley wrote:
>>> mlg3 <m_l_g3@yahoo.com> wrote:
>>>> Andrew Haley wrote:
>>> <formalism elided>
>> :-(
>>
>>>> (that is, if words in a colon definition do not read or modify the
>>>> return stack, a call of the definition is equivalent to inline
>>>> substitution of the definition's body).
>>> Quite so: this also means that fiddling with the return stack may
>>> destroy this property.  For example,
>> [sigh]
>>
>> The context was important.
> 
> As far as I could see the formalism told us in a formal way what we
> already knew.  Perhaps I missed something, though.  Were there some
> uncommon insights?
> 
>>> *** The result of return address manipulations may be predicted
>> at compile-time ***
>>
>> And this is why the formalism is possible.
> 
> But what is it for?  I don't understand where this is going.
> 
> Andrew.

ok,

>>> Theorem
>>>
>>> x == nest[ R1( x ) exit ]

(again that theorem)

Reminder.
ForAll N:
N >R R1( x ) == x N >R



Consequence 2: the correct algorithm of inlining


It requires that you track the return stack values
inside the colon definition's body.

The colon definition looks as
: foo z ;


The following cases are possible:

1. The return stack is left in the original state,
the return address and the values beneath it are not
read or written. It is ok to move the definition's
body into the calling definition as is.

It is the case that z == R1( z )


2. The return stack top is moved off the return stack and
later placed back, after something is done with the return
stack items beneath it. (Finally, the return stack top is
consumed by EXIT compiled by ; (semi-colon).)

It is easy to generate code for such y that
R1( y ) == z

(This y is the code that must be placed in the calling
definition's body in place of foo.
Indeed,
    y == nest[ R1( y ) exit ] == nest[ z exit ] == foo
)

For r-stack primitives that access (that is, move)
the return address (the top return stack item),
no code is generated.
For r-stack primitives that access other return stack
items, the code is generated as if the top return stack
item did not exist.

For example,
: rswap r> r> r> swap >r >r >r ;

rswap == r> r> swap >r >r

One mode example.

( x -- y ) ( r: y -- x )
: >r> r> swap r> swap >r swap >r ;

The generated code will be:
( r> ) ( swap ) r> swap >r ( swap ) ( >r )

The removed words are shown in parens; they are removed
because they had to manipulate the return address that
is eliminated. The effect of R> was ( x -- x ra ) but
without ra it is just ( x -- x ), that is, NOOP.
The same applies to SWAP ( x ra -- ra x ), without
ra it becomes NOOP.

Alternative definition of the same word:

( x -- y ) ( r: y -- x )
: >r> r> r> swap rot >r >r ;

The generated code will be:
( r> ) r> ( swap ) swap >r ( >r )

The first SWAP disappears because it has to swap
the return address with another value, and the
return address disappears, so SWAP becomes a NOOP.
ROT becomes SWAP because it originally did
( x y ra -- y ra x ) and after ra disappears,
what must be done becomes ( x y -- y x ).

I think you already have mentioned that
 >r> == nest[ r> r> swap rot >r >r ; ] ==
r[ ... ] r> r> swap rot >r >r ; ==
r> r[ ... ] rot >r >r ; ==
r> swap >r r[ ... ] >r ; == r> swap >r


3. The return stack is left in the original state
up to the moment that the return address is passed
to EXIT inside z. You may either not inline such
definition or transform it into an equivalent
single-exit form. (And the transformed code will
fall into category 1.)

Example.
: MAX 2dup > if drop exit then nip ;
To inline MAX, you first need to transform it into
an equivalent of
: max 2dup > if drop else nip then ;


4. All other cases. That is, the return address
is not what is passed to EXIT. Or it is passed,
but not to EXIT (and EXIT may never receive control).
There is a control transfer. You may generate optimized
code even for this case, but you will have to do code
transformations. Note that in many cases it is not
possible to completely eliminate the call (for example,
you may eliminate control transfer, but will still
have to push an address onto the return stack).

In this text, I recommend not to inline such
definitions. (Because else I would have to
give an exhaustive explanation of how to
generate control transfers for such stuff.)

Example.
: branch r> @ >r ;
You may either leave this definition as it is (that
is, call it rather than inline), or transform it into
a control transfer.

For subroutine-threaded code, the result might be
something like
    jmp [address]
address: dw destination

Example.
: give> r> ;
I think, now you see why I am not willing to give
recommendations for the general case.
Better leave this definition as it is.
The generated native code might look as:
\ give> bar baz quz
   spush address
   exit
address:
   bar
   baz
   qux

Example.
: enter >r ;
The easiest thing is to leave it as it is.
If you nevertheless want to generate optimized code,
you will have to generate an equivalent of a call
(the 'nest' action), and then transfer control to the
address at the stack top.

\ foo enter bar
    foo
    rpush address
    spop R1
    jump R1
address:
    bar

Note that in the last two examples you can generate
inline code, but you cannot completely eliminate
the call: the RPUSH ADDRESS command is a piece
of that call.



Are definitions that use return address manipulations
worth generating optimized code? Such words likely
implement control structures, and the control structures
tend to be executed in loops. On the other hand, in
many cases custom control structures are not needed.
(As least, until we begin to implement custom control
structures for parallel execution on multi-core
processors.)

Anyway, whether or not you choose to generate optimized
code, the return address manipulations _do_ deserve
generation of _correct_ code.

[toc] | [prev] | [next] | [standalone]


#4688

FromOuatu Bogdan <ouatubi@gmail.com>
Date2011-08-10 13:22 +0000
Message-ID<j1u0mi$c4n$2@dont-email.me>
In reply to#4607
Hi!

On Sat, 06 Aug 2011 01:49:07 +0400, mlg3 wrote:

> although both lead to equally untraditional, from an average
> specialist's viewpoint, code, and both are backed by solid mathematical
> background.

And that convinced me that 'return stack manipulation of addresses' is 
not just a technique but knowledge.  I think that knowledgeable use of 
return addresses should not be underestimated.
 
> Are you still with me? ;-)

Yes.  Thanks for post, theory presented is backed by examples and more 
understandable than presented euroforth papers that sometimes are a bit 
too academic.  

Have a nice day,
humptydumpty

[toc] | [prev] | [next] | [standalone]


#4608

FromZbiggy <zbigniew2011REMOVE@gmail.REMOVE.com>
Date2011-08-06 01:12 +0200
Message-ID<slrnj3p5pu.cho.zbigniew2011REMOVE@Tichy.myhome.org>
In reply to#4595
In comp.lang.forth, Andrew Haley wrote:

> I think so.  There is the matter of whether, say, return stack
> manipulation is more useful than tail call optimization.  I'd choose
> tail call optimization every time, but IME code that (ab)uses the
> return stack for control flow is hard to understand and maintain.
> Whatever the problem, there's almost always a better way to do it than
> fiddling with the R-stack.

Maybe you're right - I understand, that the examples in "Starting Forth"
weren't recommendations for "how this should be done", but rather the
illustrations of return stack functionality and of structure of the words.

But my point was rather: if possible, I would rather expect return addresses
still on "traditional" return stack - as on "standard place" in case of most
Forth compilers - instead of searching "where is it this time?".

>> Andy Valencia, ForthOS creator, proposed to use larger blocks - 4096
>> instead of "traditional" 1024 bytes - and it does make sense,
>> because meanwhile the HDD vendors changed the size of basic sector
>> unit to... 4096 bytes (from 512).
>
> Yes, it makes very good sense to do this.  However, given a 4096-byte
> block buffer, you can easily get a compatible BLOCK with
>
>: block ( n - a)   4 /mod big-block  swap 1024 * + ;
>
> So, there is no need for application code to be affected by the use of
> big blocks.

Right, but this is a "workaround for today"; and since during following
years we simply won't be able to buy an HDD with 512 byte sector, the
mentioned change seems to be logical consequence.

Of course it won't have any real meaning to compilers (most Forth compilers
of today), that aren't using "native" blocks, but "block files" instead.
-- 
...but I'm ready to learn ('bout) of the power of Forth

[toc] | [prev] | [next] | [standalone]


#4612

Fromcoos haak <chforth@hccnet.nl>
Date2011-08-06 02:37 +0200
Message-ID<f8hafrkn3qwu.1g2y8vgp00gbv$.dlg@40tude.net>
In reply to#4608
Op 6 Aug 2011 01:12:34 +0200 schreef Zbiggy:

<snip>
> Right, but this is a "workaround for today"; and since during following
> years we simply won't be able to buy an HDD with 512 byte sector, the
> mentioned change seems to be logical consequence.

All disks I have used till now had 512 byte *physical* sectors, an OS might
use them in chunks of 8 or something, it still does not matter for a
program. The 1024 size of a Forth block is a *logical* size.

Yes, it's possible to access physical sectors, e.g. on a floppy disk, but I
think it would not be sensible to do that on a terabyte disk.

-- 
Coos

CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html 

[toc] | [prev] | [next] | [standalone]


#4620

FromZbiggy <zbigniew2011REMOVE@gmail.REMOVE.com>
Date2011-08-06 12:54 +0200
Message-ID<slrnj3qet8.2sb.zbigniew2011REMOVE@Tichy.myhome.org>
In reply to#4612
In comp.lang.forth, coos haak wrote:

> All disks I have used till now had 512 byte *physical* sectors,

AFAIK presently (all? Or just "most"?) HDDs still have some "translation
chip" built-in, to be seen by older hardware as having 512 bytes per sector.

Can't examine it, as I'm using - like for today - quite small disks.

> an OS might
> use them in chunks of 8 or something, it still does not matter for a
> program. The 1024 size of a Forth block is a *logical* size.

Why exactly this size of a block you see as "logical"?

> Yes, it's possible to access physical sectors, e.g. on a floppy disk, but
> I think it would not be sensible to do that on a terabyte disk.

Why not, actually? If we mean e.g. using blocks as kind of "virtual memory",
direct use of physical sectors means faster access.
-- 
...but I'm ready to learn ('bout) of the power of Forth

[toc] | [prev] | [next] | [standalone]


#4621

Fromalextangent <blog@rivadpm.com>
Date2011-08-06 12:15 +0100
Message-ID<vbadnQqVlPvAv6DTnZ2dnUVZ8sudnZ2d@bt.com>
In reply to#4620
On 2011-08-06 11:54, Zbiggy wrote:
> In comp.lang.forth, coos haak wrote:
>
>> All disks I have used till now had 512 byte *physical* sectors,
>
> AFAIK presently (all? Or just "most"?) HDDs still have some "translation
> chip" built-in, to be seen by older hardware as having 512 bytes per sector.
>
> Can't examine it, as I'm using - like for today - quite small disks.
>
>> an OS might
>> use them in chunks of 8 or something, it still does not matter for a
>> program. The 1024 size of a Forth block is a *logical* size.
>
> Why exactly this size of a block you see as "logical"?
>
>> Yes, it's possible to access physical sectors, e.g. on a floppy disk, but
>> I think it would not be sensible to do that on a terabyte disk.
>
> Why not, actually? If we mean e.g. using blocks as kind of "virtual memory",
> direct use of physical sectors means faster access.

Forget physical sectors. 4K will become the norm, and not all disks are 
512 byte sector size; some are 520 bytes. Flash memory will also become 
the predominant technology, and that needs a completely new way of think 
about writing to "disk", since the block erase overhead is considerable.

[toc] | [prev] | [next] | [standalone]


#4614

FromElizabeth D Rather <erather@forth.com>
Date2011-08-05 22:23 -0500
Message-ID<EaednVOh3bw-LqHTnZ2dnUVZ_qednZ2d@supernews.com>
In reply to#4608
On 8/5/11 6:12 PM, Zbiggy wrote:
> In comp.lang.forth, Andrew Haley wrote:
>
>> I think so.  There is the matter of whether, say, return stack
>> manipulation is more useful than tail call optimization.  I'd choose
>> tail call optimization every time, but IME code that (ab)uses the
>> return stack for control flow is hard to understand and maintain.
>> Whatever the problem, there's almost always a better way to do it than
>> fiddling with the R-stack.
>
> Maybe you're right - I understand, that the examples in "Starting Forth"
> weren't recommendations for "how this should be done", but rather the
> illustrations of return stack functionality and of structure of the words.
>
> But my point was rather: if possible, I would rather expect return addresses
> still on "traditional" return stack - as on "standard place" in case of most
> Forth compilers - instead of searching "where is it this time?".

As a user, you don't need to search, because real return addresses are 
not intended to be user-accessible. They are a system facility, as are 
others (such as code space and name fields).  It inappropriate to think 
that you can manipulate return addresses in any way that is predictably 
the same from one implementation to another.

FORTH83 mandated implementation details such as return addresses, stack 
widths (only 16 bits!), layout of definitions, etc.  In the Forth94 
process, we realized that this was a terrible mistake, and took the firm 
step towards specifying behavior, not implementation.  The result has 
been significantly faster and more powerful Forth systems. The cost, of 
course, is that users can't write code that depends on implementation 
details.  IMO it's a win.

Forth offers plenty of ways to control program flow.  You don't need to 
play cute tricks with return addresses.

Cheers,
Elizabeth

-- 
==================================================
Elizabeth D. Rather   (US & Canada)   800-55-FORTH
FORTH Inc.                         +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================

[toc] | [prev] | [next] | [standalone]


#4617

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2011-08-06 03:17 -0500
Message-ID<t_6dnc1zyNw-ZaHTnZ2dnUVZ7oudnZ2d@supernews.com>
In reply to#4608
Zbiggy <zbigniew2011REMOVE@gmail.remove.com> wrote:
> In comp.lang.forth, Andrew Haley wrote:
>  
>>> Andy Valencia, ForthOS creator, proposed to use larger blocks - 4096
>>> instead of "traditional" 1024 bytes - and it does make sense,
>>> because meanwhile the HDD vendors changed the size of basic sector
>>> unit to... 4096 bytes (from 512).
>>
>> Yes, it makes very good sense to do this.  However, given a 4096-byte
>> block buffer, you can easily get a compatible BLOCK with
>>
>>: block ( n - a)   4 /mod big-block  swap 1024 * + ;
>>
>> So, there is no need for application code to be affected by the use of
>> big blocks.
> 
> Right, but this is a "workaround for today"; and since during
> following years we simply won't be able to buy an HDD with 512 byte
> sector, the mentioned change seems to be logical consequence.

It's not a workaround at all.  In the past, Forths used to have
different sized blocks, and there was a constant B/BUF that was used.
Changing BLOCKs to a standard size across all implementations, no
matter what their physical block size, was an improvement.  To go back
to the old days, where you had to cope with variable block sizes, is a
regression.

Andrew.

[toc] | [prev] | [next] | [standalone]


#4609

FromJulian Fondren <ayrnieu@gmail.com>
Date2011-08-05 19:02 -0500
Message-ID<8662mb5rw8.fsf@gmail.com>
In reply to#4595
Andrew Haley <andrew29@littlepinkcloud.invalid> writes:
> I think so.  There is the matter of whether, say, return stack
> manipulation is more useful than tail call optimization.  I'd choose
> tail call optimization every time, but IME code that (ab)uses the
> return stack for control flow is hard to understand and maintain.
> Whatever the problem, there's almost always a better way to do it than
> fiddling with the R-stack.

Can you elaborate on your IME?  I'm not being flippant; I'm privately
becoming more and more confident that fiddling with the return stack is
a huge readability and therefore maintainability win, so I'd appreciate
a course correction.

I recall that you prefer immediate words for a good subset of what
return-stack words can do.  E.g.,

  : with-base ( u -- )
    r> BASE @ >r swap BASE !
    ['] call catch r> BASE ! throw ;

  : base[ ( u -- )
    postpone BASE postpone @ postpone >r
    postpone BASE postpone ! ; immediate
  : ]base ( -- )
    postpone r> postpone BASE postpone ! ; immediate

  : x1 ( ... "to end of line" -- ... )
    16 with-base 0 parse evaluate ;

  : x2 ( ... "to end of line" -- ... )
    16 base[ 0 parse evaluate ]base ;

Return-stack access must be balanced after WITH-BASE and between BASE[
]BASE ; only WITH-BASE will reset the BASE if there's an exception
(maybe not desirable, but when it isn't WITH-BASE can be easily defined
to not do this).  In the presence of tail-call optimization, WITH-BASE
_can_ be used to do perverse things that only sometimes work

  : in-hex 16 with-base ;
  : x3 ( c-addr u -- )
    in-hex evaluate ;    \ only works if WITH-BASE was JMP'd to.

, but I oppose that.  You can take any feature too far.  You can have a
Forth word that accepts twenty arguments on the data stack.

But, these are so similar; how is only one of them hard to understand
and maintain?

Another class of return-stack words are words that validate some input:

  : -too-small ( c-addr u -- c-addr u | *returns from caller )
    dup 2 > ?exit 2drop r>drop ;
  : -not-forth ( c-addr u -- c-addr u | *returns from caller )
    2dup 2 - + 2 s" .f" compare(nc) 0= ?exit 2drop r>drop ;

  : .forth-file ( dirent-addr -- )
    USING dirent filename zcount
    -too-small -not-forth type space ;

"If it's not too small, and if it's not not forth, TYPE SPACE it."
Maybe S" .f" HAS-EXT; would be a better name, with the semicolon to
suggest the potential exit.

With immediate wordS:

  : -too-small ( c-addr u -- c-addr u | *exit )
    postpone dup 2 postpone literal postpone <=
    postpone if postpone 2drop postpone exit postpone then ; immediate

And so on.  It's used in exactly the same way, it has exactly the same
issue of "to test this, I need to use it within a temporary definition",
and it could be neater with a macro to replace the POSTPONEs.

So these are _exactly the same_.  How is the only of them hard to
understand and maintain?

I could have also "not done that" - have written

  : .forth-file ( dirent-addr -- )
    USING dirent filename zcount
    dup 2 <= if 2drop exit then
    2dup 2 - + 2 s" .f" compare(nc) if 2drop exit then
    type space ;

Or:

  : .forth-file ( dirent-addr -- )
    USING dirent filename zcount ( c-addr u )
    dup 2 > if  2dup 2 - + 2 s" .f" compare(nc) 0= if
      type space exit
    then then 2drop ;

Or:

  : .forth-file ( dirent-addr -- )
    USING dirent filename zcount 
    2dup long-enough? if 2dup is-forth? if
      type space exit
    then then 2drop ;

The last is nice and conventional, but is the return-stack version
really any harder to read?

Which classes of return-stack-fiddling word do you object to?

[toc] | [prev] | [next] | [standalone]


#4618

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2011-08-06 04:01 -0500
Message-ID<Mq2dnfXtqqWanqDTnZ2dnUVZ8v-dnZ2d@supernews.com>
In reply to#4609
Julian Fondren <ayrnieu@gmail.com> wrote:
> Andrew Haley <andrew29@littlepinkcloud.invalid> writes:
>> I think so.  There is the matter of whether, say, return stack
>> manipulation is more useful than tail call optimization.  I'd choose
>> tail call optimization every time, but IME code that (ab)uses the
>> return stack for control flow is hard to understand and maintain.
>> Whatever the problem, there's almost always a better way to do it than
>> fiddling with the R-stack.
> 
> Can you elaborate on your IME?  I'm not being flippant; I'm privately
> becoming more and more confident that fiddling with the return stack is
> a huge readability and therefore maintainability win, so I'd appreciate
> a course correction.
> 
> I recall that you prefer immediate words for a good subset of what
> return-stack words can do.  E.g.,
> 
>  : with-base ( u -- )
>    r> BASE @ >r swap BASE !
>    ['] call catch r> BASE ! throw ;

What is CALL ?

>  : base[ ( u -- )
>    postpone BASE postpone @ postpone >r
>    postpone BASE postpone ! ; immediate
>  : ]base ( -- )
>    postpone r> postpone BASE postpone ! ; immediate
> 
>  : x1 ( ... "to end of line" -- ... )
>    16 with-base 0 parse evaluate ;
> 
>  : x2 ( ... "to end of line" -- ... )
>    16 base[ 0 parse evaluate ]base ;

This doesn't do anything nonstandard with the R-stack, as far as I can
see.  It seems very elaborate for what it does, but I suppose if you
have a very great deal of BASE-changing it might be worthwhile.  On
the other hand, it might be clearer simply to use WITH-BASE wthout all
the syntactic sugar.

> Return-stack access must be balanced after WITH-BASE and between BASE[
> ]BASE ; only WITH-BASE will reset the BASE if there's an exception
> (maybe not desirable, but when it isn't WITH-BASE can be easily defined
> to not do this).  In the presence of tail-call optimization, WITH-BASE
> _can_ be used to do perverse things that only sometimes work
> 
>  : in-hex 16 with-base ;
>  : x3 ( c-addr u -- )
>    in-hex evaluate ;    \ only works if WITH-BASE was JMP'd to.
> 
> , but I oppose that.  You can take any feature too far.  You can have a
> Forth word that accepts twenty arguments on the data stack.
> 
> But, these are so similar; how is only one of them hard to understand
> and maintain?

I didn't say that.  Fiddling with the R-stack is just one way to
confuse the reader.  There are many more.  :-)

> Another class of return-stack words are words that validate some input:
> 
>  : -too-small ( c-addr u -- c-addr u | *returns from caller )
>    dup 2 > ?exit 2drop r>drop ;
>  : -not-forth ( c-addr u -- c-addr u | *returns from caller )
>    2dup 2 - + 2 s" .f" compare(nc) 0= ?exit 2drop r>drop ;
> 
>  : .forth-file ( dirent-addr -- )
>    USING dirent filename zcount
>    -too-small -not-forth type space ;
> 
> "If it's not too small, and if it's not not forth, TYPE SPACE it."
> Maybe S" .f" HAS-EXT; would be a better name, with the semicolon to
> suggest the potential exit.
> 
> With immediate wordS:
> 
>  : -too-small ( c-addr u -- c-addr u | *exit )
>    postpone dup 2 postpone literal postpone <=
>    postpone if postpone 2drop postpone exit postpone then ; immediate
> 
> And so on.  It's used in exactly the same way, it has exactly the same
> issue of "to test this, I need to use it within a temporary definition",
> and it could be neater with a macro to replace the POSTPONEs.
> 
> So these are _exactly the same_.  How is the only of them hard to
> understand and maintain?

They're both hard to understand and maintain.  What's wrong with

   -too-small if  -not-forth if  type space  then then

?  Golly, even a Forth beginner could understand what that does .  We
can't have that!  :-)

> I could have also "not done that" - have written
> 
>  : .forth-file ( dirent-addr -- )
>    USING dirent filename zcount
>    dup 2 <= if 2drop exit then
>    2dup 2 - + 2 s" .f" compare(nc) if 2drop exit then
>    type space ;
> 
> Or:
> 
>  : .forth-file ( dirent-addr -- )
>    USING dirent filename zcount ( c-addr u )
>    dup 2 > if  2dup 2 - + 2 s" .f" compare(nc) 0= if
>      type space exit
>    then then 2drop ;
> 
> Or:
> 
>  : .forth-file ( dirent-addr -- )
>    USING dirent filename zcount 
>    2dup long-enough? if 2dup is-forth? if
>      type space exit
>    then then 2drop ;
> 
> The last is nice and conventional, but is the return-stack version
> really any harder to read?

Yes.  Conventional is good.  The maintenance programmer's time is
valuable.  Think about it for a moment: on first sight, how long would
it take the reader to figure out what these programs do?  Remember,
you're writing code to be read.

There's an old saying that appears in many forms, one of which is
"Don't use a ten dollar word when a nickel one will do."  This is good
advice when writing English, and terrific advice when programming.
People learning English often use heavyweight synonyms and complex
structures they've learned.  That's good while learning, but it's also
a sure sign of someone not very fluent with the language.

A maintenance programmer has a reasonable expectation that a
heavyweight technique will only be used when demanded by a heavyweight
problem.  When they find otherwise there's a kind of cognitive
dissonance (talk about 10-dollar words!) that forces a break in
concentration.  It also forces them to step away from the problem they
were trying to solve in order to learn how your custom control
structures work.

This is not general advice against custom control structures, which
are one of Forth's strengths.  But it's important to realize that,
while they can simplify code and make it more readable, there is a
cost.  A wise programmer balances costs and benefits, and uses
powerful techniques sparingly.

Andrew.

[toc] | [prev] | [next] | [standalone]


#4625 — return-address manipulation (was: multi-threading in Forth?)

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2011-08-06 16:35 +0000
Subjectreturn-address manipulation (was: multi-threading in Forth?)
Message-ID<2011Aug6.183535@mips.complang.tuwien.ac.at>
In reply to#4618
Andrew Haley <andrew29@littlepinkcloud.invalid> writes:
>Julian Fondren <ayrnieu@gmail.com> wrote:
>>  : with-base ( u -- )
>>    r> BASE @ >r swap BASE !
>>    ['] call catch r> BASE ! throw ;
>
>What is CALL ?
>
>>  : base[ ( u -- )
>>    postpone BASE postpone @ postpone >r
>>    postpone BASE postpone ! ; immediate
>>  : ]base ( -- )
>>    postpone r> postpone BASE postpone ! ; immediate
>> 
>>  : x1 ( ... "to end of line" -- ... )
>>    16 with-base 0 parse evaluate ;
>> 
>>  : x2 ( ... "to end of line" -- ... )
>>    16 base[ 0 parse evaluate ]base ;
[...]
>On
>the other hand, it might be clearer simply to use WITH-BASE wthout all
>the syntactic sugar.

But WITH-BASE is the word that uses return address manipulation.

Another alternative is to write

: eval-rest ( ... -- ... )
  0 parse evaluate ;

: x3 ( ... "to end of line" -- ... )
   ['] eval-rest 16 base-execute ;

And of course, if we had nested colon defs (called quotations, blocks,
or lambda expressions in other languages). we could write this as follows:

: x4 ( ... "to end of line" -- ... )
  [: 0 parse evaluate :] 16 base-execute ;

One disadvantage I see for the WITH-BASE variant here is that you have
to wrap it in another colon definition if you want to do anything
after restoring BASE.

>>  : .forth-file ( dirent-addr -- )
>>    USING dirent filename zcount
>>    -too-small -not-forth type space ;
[...]
>They're both hard to understand and maintain.  What's wrong with
>
>   -too-small if  -not-forth if  type space  then then

It's longer.

>>  : .forth-file ( dirent-addr -- )
>>    USING dirent filename zcount 
>>    2dup long-enough? if 2dup is-forth? if
>>      type space exit
>>    then then 2drop ;
>> 
>> The last is nice and conventional, but is the return-stack version
>> really any harder to read?
>
>Yes.  Conventional is good.  The maintenance programmer's time is
>valuable.  Think about it for a moment: on first sight, how long would
>it take the reader to figure out what these programs do?

That's a good question.  If the maintenance programmer is familiar
with the idiom, does it take the maintenance programmer longer to
understand the shorter code?

>A maintenance programmer has a reasonable expectation that a
>heavyweight technique will only be used when demanded by a heavyweight
>problem.  When they find otherwise there's a kind of cognitive
>dissonance (talk about 10-dollar words!) that forces a break in
>concentration.  It also forces them to step away from the problem they
>were trying to solve in order to learn how your custom control
>structures work.

You are assuming that the maintenance programmer is not familiar with
the idiom.  The same argument could be used against any programming
technique and any programming language: Just assume that the
programmer has to learn the technique/language first, and your
argument will eliminate the technique/language from consideration.  I
don't think that this argument is useful.

I am not convinced that return address manipulation offers enough over
"conventional" to make it worthwhile to teach and learn the idioms
that use it, or whether we should better concentrate on adding
features like nested colon defs.  But in any case, I would like to see
more useful arguments in the discussion.

- 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 2011: http://www.euroforth.org/ef11/

[toc] | [prev] | [next] | [standalone]


#4635 — Re: return-address manipulation

FromGerry Jackson <gerry@jackson9000.fsnet.co.uk>
Date2011-08-07 11:45 +0100
SubjectRe: return-address manipulation
Message-ID<j1lqbi$u2h$1@dont-email.me>
In reply to#4625
On 06/08/2011 17:35, Anton Ertl wrote:

[...]

> I am not convinced that return address manipulation offers enough over
> "conventional" to make it worthwhile to teach and learn the idioms
> that use it, or whether we should better concentrate on adding
> features like nested colon defs.  But in any case, I would like to see
> more useful arguments in the discussion.
>

A few months ago I made an effort to understand some return stack 
manipulation techniques, specifically those described in the writings of 
mlg3 at:
https://groups.google.com/group/comp.lang.forth/browse_frm/thread/b8a35c3014a40d40/8854a1b225d5bace?hl=en&lnk=gst&q=foreachopcode#8854a1b225d5bace
and http://www.forth.org.ru/~mlg/ef94/ef94-2-paper.txt

To apply the technique I used the Spykerman Sudoku solver ( available 
at: 
http://www-personal.umich.edu/~williams/archive/forth/utilities/sudoku.fs) 
as a testbed where I recoded a few words to see if it was easy, more 
readable etc.

To give an example, I recoded the word that displays the Sudoku grid. 
The original code was:

: .sudokugrid
   CR CR
   sudokugrid
   81 0 do
     dup i + c@ . ." "
     i 1+
       dup 3 mod 0= if
          dup 9 mod 0= if
             CR
             dup 27 mod 0= if
               dup 81 < if ." ------+-------+------" CR then
             then
          else
            ." ! "
          then
       then
     drop
   loop
   drop
   CR
;

which, to me least, is not a good example of well-written Forth as it is 
difficult to read, but could probably be rewritten to be more readable 
in conventional Forth (perhaps someone would like to try?). My rewrite was:

: for-each-square  pro 81 0 do i cont loop ;
: .square   ( grid sq -- grid sq )  2dup + c@ . ;
: boxright  ( sq -- sq | )  dup 3 mod if drop fail then ;
: gridright ( sq -- sq | )  dup 9 mod if drop ." ! " fail then cr ;
: boxbase   ( sq -- )  80 min 27 mod 0= if ." ------+-------+------" cr 
then ;

: .sudokugrid  ( -- )
    cr cr sudokugrid                       ( -- grid )
    start for-each-square                  ( -- grid sq )
             .square 1+
             boxright gridright boxbase    ( -- grid )
    emerge drop cr                         ( -- )
;

where the words PRO CONT FAIL START and EMERGE are all described in the 
above references.  If anyone is interested I can provide definitions for 
these that work with GForth.

This could be improved e.g. by renaming START as FOR-EACH and EMERGE as 
END-FOR and better word names for the factors.

As a generator of square numbers, FOR-EACH-SQUARE was re-used in the 
recoding of the other words that I completed.

How easy was it? Of the four words I recoded, three were easy, the 
fourth, called SOLVER, took several attempts to get right. That may have 
been a candidate for mlg3's AMONG ... EACH ... ITERATE ... control 
structure (but I never got that far - I'm not sure that structure can be 
implemented in Forth alone).

-- 
Gerry

[toc] | [prev] | [next] | [standalone]


Page 2 of 4 — ← Prev page 1 [2] 3 4  Next page →

Back to top | Article view | comp.lang.forth


csiph-web