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

Request for comments on a first forth program

566 views
Skip to first unread message

Christian Kellermann

unread,
Jul 19, 2013, 5:39:11ā€ÆAM7/19/13
to
Dear group readers,

I have been re-reading my books on Forth lately and decided to get into
learning it finally. One of the first results have been attached to this
article and I would like to hear your criticism about it. (If this is a
faux pas on this list please ignore this request).

It's a small and silly implementation of the BSD game robots. You ("@")
have to escape some robots ("R") and win by luring them into holes
("*").

I have used GNU Forth to get started, mostly because I don't know any
forth system well enough (besides a little dabbling with amforth) to
judge any of the existing systems.

Things that did surprise me:

* Working with the stack is not hard at all
* Debugging Forth words has been easier than anticipated. I did not use
the gforth debugging facilities though.
* A bad choice of data structures is punished more heavily in forth due
to unnecessary stack juggling involved than in other programming
languages.

What I don't like in my program:

* The word names are probably way too verbose for forthers. Comming from
scheme I am quite accustomed to lengthy names and could not help it so
far.
* Redundant code due to my ignorance of the forth metaprogramming
facilities. I need to read up on these.
* Unclear when to use cells or bytes as an access unit for accessing
buffers.
* What's the preferred coding style for longer words? Or is this a sign
that my word is too convoluted and should be refactored?
* The access words for the values seem too verbose, are these even
necessary?

Also I have the general feeling that it could be rewritten to take up
less words. I probably choose a bad data representation.

What are your thoughts?

Looking forward to hearing them,

Christian

--8<---------------cut here---------------start------------->8---
require random.fs

\ statistics
0 value #moves
: #moves+ #moves 1+ to #moves ;
: #moves- #moves 1- to #moves ;
3 value #teleports
: #teleports+ #teleports 1+ to #teleports ;
: #teleports- #teleports 1- to #teleports ;

\ the board
25 constant board-rows
80 constant board-cols

: random-xy board-cols random board-rows random ;

board-rows board-cols * constant board-dimension
create board board-dimension allot
: xy->board ( n1 n2 -- i ) board-cols * + ;
: board@ ( n1 n2 -- c ) xy->board board + c@ ;
: board! ( c n1 n2 -- ) xy->board board + c! ;

\ drawing words
char @ constant player-sym
char R constant robot-sym
char o constant hole-sym
32 constant floor-sym
char | constant wall-sym

: border ( -- ) [char] + emit board-cols 0 do [char] - emit loop [char] + emit ;
: wall ( -- ) wall-sym emit ;
: board-reset ( -- ) board-dimension 0 do floor-sym board i + ! loop ;
: board-print ( -- )
page border cr wall
board-dimension 0 do
i i board-cols mod 0= 0< and
if wall cr wall then
board i + @ emit
loop
wall cr border cr ;

\ player
create (player) 2 cells allot
: player@ ( -- x y ) (player) 2@ ;
: player! ( x y -- ) (player) 2! ;
: new-position ( x1 y1 dx dy -- x2 y2 ) rot + >r + r> ;
: valid-position? ( x y - b )
dup 0>= swap board-rows < and swap
dup 0>= swap board-cols < and and ;
\ xy->board dup board-dimension < swap 0 >= and ;

: up ( -- dx dy ) 0 -1 ;
: down ( -- dx dy ) 0 1 ;
: left ( -- dx dy ) -1 0 ;
: right ( -- dx dy ) 1 0 ;
: update-player ( x y -- ) floor-sym player@ board! player-sym rot rot 2dup player! board! ;
: move ( dx dy -- ) player@ 2swap new-position 2dup valid-position? if update-player else 2drop then ;

\ hole words
10 constant #holes
create holes #holes 2* cells allot
: hole@ ( i -- x y ) 2* cells holes + 2@ ;
: hole! ( x y i -- ) 2* cells holes + 2! ;


\ robot words
2 constant #robots
\ robots occupy 3 cells: x y alive?
create robots #robots 3 * cells allot
: robot@ ( i -- x y ) 3 * cells robots + 2@ ;
: robot! ( x y i -- ) 3 * cells robots + 2! ;
: robot-alive ( b i -- ) 3 * cells 2 cells + robots + ! ;
: robot-alive? ( i -- b ) 3 * cells 2 cells + robots + @ ;
: #robots-alive ( -- n ) 0 #robots 0 ?do i robot-alive? if 1+ then loop ;
: update-robot ( x y i -- ) >r 2dup i robot@ floor-sym rot rot board! 2dup robot-sym rot rot board! r> robot! ;
: distance ( x1 y1 x2 y2 -- x2-x1 y2-y1) >r rot - r> rot - swap ;
: sign ( n1 -- n2 ) dup 0= if drop 0 else dup abs / then ;
: direction ( d1 d2 -- dx dy ) sign swap sign ;
: towards-player ( x y -- x' y' ) 2dup player@ distance direction new-position ;
: collision? ( x1 y1 x2 y2 -- b ) xy->board rot rot xy->board = ;
: is-in-hole? ( x y -- b )
xy->board false #holes 0 do over i hole@ xy->board = or loop swap drop ;
: move-robot ( i -- )
dup >r robot@ towards-player 2dup is-in-hole?
if false i robot-alive floor-sym r> robot@ board!
else r> update-robot then ;
: move-robots ( -- ) #robots 0 ?do i robot-alive? if i move-robot then loop ;
: any-collision? ( -- b ) false #robots 0 ?do i robot-alive? if i robot@ player@ collision? or then loop ;

\ game routines init, loop
: init-robots #robots 0 ?do robot-sym random-xy 2dup i robot! board! true i robot-alive loop ;
: init-holes #holes 0 do hole-sym random-xy 2dup i hole! board! loop ;
: init-player random-xy 2dup player! update-player 0 to #moves 3 to #teleports ;
: reset-game board-reset init-player init-robots init-holes ;
: status-line ." moves: " #moves . ." teleports: " #teleports . ." robots: " #robots-alive . ;
: help ." h: left, j: down, k: up, l: right, t: teleport, q: quit game" cr ;
: user-input key
case
[char] h of left move endof
[char] j of down move endof
[char] k of up move endof
[char] l of right move endof
[char] q of ." Thanks for playing! " quit endof
[char] t of #teleports 0> if #teleports- random-xy 2dup update-player player! then endof
#moves-
endcase
#moves+ ;

: run reset-game
begin
move-robots board-print status-line cr help
#robots-alive 0= if ." You win!" cr bye then
any-collision? if
." You died! " cr bye
else user-input then
again ;

run
--8<---------------cut here---------------end--------------->8---

Ron Aaron

unread,
Jul 19, 2013, 6:04:46ā€ÆAM7/19/13
to
On 07/19/2013 12:39 PM, Christian Kellermann wrote:

> * The word names are probably way too verbose for forthers. Comming from
> scheme I am quite accustomed to lengthy names and could not help it so
> far.

In my opinion the word names are fine. As a general rule, I prefer to
use names which will be informative to me when I look at the code months
later. Going for short names especially has no particular advantage
unless you are on one of the more contrained Forth systems.

> * Unclear when to use cells or bytes as an access unit for accessing
> buffers.

Up to you. Cell access can be faster, but that depends on the system,
and on what you are doing with the data.

> * What's the preferred coding style for longer words? Or is this a sign
> that my word is too convoluted and should be refactored?

Could be :)

Longer words are un-preferred, mainly because they are much harder to
debug. On the other hand, it can be overly challenging to factor a
largish word when there are no clear factorizable units in it.

> * The access words for the values seem too verbose, are these even
> necessary?

You mean "robot@", etc? I like that style, because it lets you modify
the underlying storage mechanism transparently if you wish.

Nice first effort!

Andrew Haley

unread,
Jul 19, 2013, 6:28:57ā€ÆAM7/19/13
to
Christian Kellermann <ck...@pestilenz.org> wrote:
>
> What I don't like in my program:
>
> * The word names are probably way too verbose for forthers. Comming from
> scheme I am quite accustomed to lengthy names and could not help it so
> far.
> * Redundant code due to my ignorance of the forth metaprogramming
> facilities. I need to read up on these.

If you don't need a big gun, don't reach for one. You did right.

> * Unclear when to use cells or bytes as an access unit for accessing
> buffers.
> * What's the preferred coding style for longer words? Or is this a sign
> that my word is too convoluted and should be refactored?

The simple rule is: one definition, one line. When you're an
experienced Forther you get to ignore this rule!

> * The access words for the values seem too verbose, are these even
> necessary?

Perhaps/ perhaps not. Remove 'em and see.

> Also I have the general feeling that it could be rewritten to take up
> less words. I probably choose a bad data representation.
>
> What are your thoughts?

I think it's outstanding for a first Forth program. You haven't
fallen into the trap of writing long definitions. You have given
everything a sensible name. It's nice to read. You haven't played
silly tricks.

Really, most experienced Forthers wouldn't have done much better.

Andrew.

m.a.m....@tue.nl

unread,
Jul 19, 2013, 6:56:51ā€ÆAM7/19/13
to
On Friday, July 19, 2013 11:39:11 AM UTC+2, Christian Kellermann wrote:
> Dear group readers, I have been re-reading my books on Forth lately
> and decided to get into learning it finally. One of the first results
> have been attached to this article and I would like to hear your
> criticism about it.

It is a great first program. I could read through it almost
completely without needing to look up previous words (i.e. good
word choice, clear structure).

Towards the end I couldn't understand what the Robot strategy was,
so I ran the code. The strategy is too simple, but I now know where
to change it, which is A Good Thing. Some comments at the start
and at each section might be nice.

RANDOM is a strange gForth-ism. Use CHOOSE (Brody). (Indeed, I
crashed immediately and had to read the code).

I consider BYE a very bad word in Forth!

Your program had 130 items on it after my first game. I think
there should be a 2drop for the player or robot position somewhere.

Don't erase the screen, we have AT-XY in Forth.
You can also use cursor keys with ANS Forth 20xx.

Maybe try >R and R>, it may simplify the stack some more.

It is not standard, but it helps to HIDE words which have no
significance to the user (the maintainer also benefits).
I use PRIVATE in iForth to do this.

If you only want to write one game the source is OK. If you plan more,
there seem to be quite a few words that could be in a separate
library. In that case CREATE DOES> might be handy for boards,
data structures etc. For now it is overkill.

Congratulations!

-marcel

m.a.m....@tue.nl

unread,
Jul 19, 2013, 7:00:09ā€ÆAM7/19/13
to
> Your program had 130 items on it after my first game

Might mean all kind of things...
"Your data stack had 130 items on it after my first game"

-marcel

Alex McDonald

unread,
Jul 19, 2013, 9:49:09ā€ÆAM7/19/13
to
On Friday, 19 July 2013 10:39:11 UTC+1, Christian Kellermann wrote:
> Dear group readers,
>
>
[ snip ]

Excellent stuff for a first attempt.

> \ robot words
> 2 constant #robots
> \ robots occupy 3 cells: x y alive?
> create robots #robots 3 * cells allot
> : robot@ ( i -- x y ) 3 * cells robots + 2@ ;
> : robot! ( x y i -- ) 3 * cells robots + 2! ;
> : robot-alive ( b i -- ) 3 * cells 2 cells + robots + ! ;
> : robot-alive? ( i -- b ) 3 * cells 2 cells + robots + @ ;


There's some factoring you could do here and elsewhere that would improve the clarity. This could be factored to

\ robot words
2 constant #robots
\ robots occupy 3 cells: x y alive?
\ size of robot slot in cells
3 cells constant slot-size
\ offset of state
2 cells constant state-off
create robots slot-size #robots * allot
: robot-slot ( i -- slot ) slot-size * robots + ;
: robot@ ( i -- x y ) robot-slot 2@ ;
: robot! ( x y i -- ) robot-slot 2! ;
: robot-state ( i -- state ) robot-slot state-off + ;
: robot-alive ( b i -- ) robot-state ! ;
: robot-alive? ( i -- b ) robot-state @ ;

For consistency with your other names, robot-alive should be robot-alive!

Mark Wills

unread,
Jul 19, 2013, 10:19:37ā€ÆAM7/19/13
to
Very nice indeed!

Next, have a go at DarkStar. Most of the Forth is written for you so it should be quite easy to port it to GForth or any Forth, for that matter.

See http://turboforth.net/tutorials/darkstar.html

rickman

unread,
Jul 19, 2013, 12:43:20ā€ÆPM7/19/13
to
On 7/19/2013 6:04 AM, Ron Aaron wrote:
> On 07/19/2013 12:39 PM, Christian Kellermann wrote:
>
>> * The word names are probably way too verbose for forthers. Comming from
>> scheme I am quite accustomed to lengthy names and could not help it so
>> far.
>
> In my opinion the word names are fine. As a general rule, I prefer to
> use names which will be informative to me when I look at the code months
> later. Going for short names especially has no particular advantage
> unless you are on one of the more contrained Forth systems.
>
>> * Unclear when to use cells or bytes as an access unit for accessing
>> buffers.
>
> Up to you. Cell access can be faster, but that depends on the system,
> and on what you are doing with the data.
>
>> * What's the preferred coding style for longer words? Or is this a sign
>> that my word is too convoluted and should be refactored?
>
> Could be :)
>
> Longer words are un-preferred, mainly because they are much harder to
> debug. On the other hand, it can be overly challenging to factor a
> largish word when there are no clear factorizable units in it.

What I have been told is, when encountering a word that is too large as
well as hard to factor, that this is a sign that the larger design is
not so good.

Refactoring is not just about making the code smaller so it is easier to
debug. Refactoring is about modularizing the problem in ways that make
the coding and debug easier. That said, I'm not the greatest at doing
this. But I do find it easier to do in Forth than I do in VHDL, lol!


>> * The access words for the values seem too verbose, are these even
>> necessary?
>
> You mean "robot@", etc? I like that style, because it lets you modify
> the underlying storage mechanism transparently if you wish.
>
> Nice first effort!

I'll start to get worried when he defines the word "I,robot". ;)

--

Rick

rickman

unread,
Jul 19, 2013, 12:51:24ā€ÆPM7/19/13
to
On 7/19/2013 6:56 AM, m.a.m....@tue.nl wrote:
> On Friday, July 19, 2013 11:39:11 AM UTC+2, Christian Kellermann wrote:
>> Dear group readers, I have been re-reading my books on Forth lately
>> and decided to get into learning it finally. One of the first results
>> have been attached to this article and I would like to hear your
>> criticism about it.
>
> Maybe try>R and R>, it may simplify the stack some more.

Just a word of caution about using the return stack. When you do, you
need to be very careful about making sure you restore the stack to its
proper state before leaving a word. Keeping your definitions small will
aid this greatly.

Another tenet of Forth programming is to thoroughly test your words
before using them. It can be a lot of work to test each word
separately, but this can save you lots of debug time chasing errors like
forgetting to restore the return stack which will cause aborts and even
crashes rather than just program errors.

--

Rick

Elizabeth D. Rather

unread,
Jul 19, 2013, 1:55:32ā€ÆPM7/19/13
to
On 7/18/13 11:39 PM, Christian Kellermann wrote:
> Dear group readers,
>
> I have been re-reading my books on Forth lately and decided to get into
> learning it finally. One of the first results have been attached to this
> article and I would like to hear your criticism about it. (If this is a
> faux pas on this list please ignore this request).

It's very good. I have seen a lot of more experienced Forth programmers
write much uglier code.

> Things that did surprise me:
>
> * Working with the stack is not hard at all
> * Debugging Forth words has been easier than anticipated. I did not use
> the gforth debugging facilities though.
> * A bad choice of data structures is punished more heavily in forth due
> to unnecessary stack juggling involved than in other programming
> languages.

Good (and accurate) observations. For most of us, after a little
practice managing the stack is very natural. I have likened it to
learning to ride a bicycle: awkward and uncomfortable at first, but when
you get the knack, it's as if you always knew.

> What I don't like in my program:
>
> * The word names are probably way too verbose for forthers. Comming from
> scheme I am quite accustomed to lengthy names and could not help it so
> far.

The names are ok, IMO.

> * Redundant code due to my ignorance of the forth metaprogramming
> facilities. I need to read up on these.

Not sure what you mean. I don't see any instances of that sort.

> * Unclear when to use cells or bytes as an access unit for accessing
> buffers.

Whichever is most convenient. I note you are frequently having to use
"cells". If you container doesn't need to hold values >256, you can get
away with bytes. I'm afraid I don't have enough time to analyze this more.

> * What's the preferred coding style for longer words? Or is this a sign
> that my word is too convoluted and should be refactored?

Your longer words aren't excessive. The key is whether there are
conceptual units that can be factored and tested. For example, in
user-input the actions in each OF...ENDOF clause are very simple. If
they were more complex, they should be factored so they could be tested
independently.

> * The access words for the values seem too verbose, are these even
> necessary?

I don't think player@ and player! were worth defining. The longer ones
are ok. up, down, etc., could have been 2CONSTANTs, although that
doesn't make a lot of difference.

> Also I have the general feeling that it could be rewritten to take up
> less words. I probably choose a bad data representation.

Experiment with some others! That's the way to learn to think about
these issues.

> What are your thoughts?

My biggest criticism is that you have some words without stack comments,
and Marcel reports stuff is being left on the stack. When you are
testing your code, it is essential that you check that every word
conforms to its stack comment behavior, and that nothing is left on the
stack that shouldn't be.

Congratulations on a good start!

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."
==================================================

Albert van der Horst

unread,
Jul 19, 2013, 2:06:47ā€ÆPM7/19/13
to
In article <26e3733d-d76f-4a4a...@googlegroups.com>,
<m.a.m....@tue.nl> wrote:
>On Friday, July 19, 2013 11:39:11 AM UTC+2, Christian Kellermann wrote:
>> Dear group readers, I have been re-reading my books on Forth lately
>> and decided to get into learning it finally. One of the first results
>> have been attached to this article and I would like to hear your
>> criticism about it.
>
>It is a great first program. I could read through it almost
>completely without needing to look up previous words (i.e. good
>word choice, clear structure).
>
>Towards the end I couldn't understand what the Robot strategy was,
>so I ran the code. The strategy is too simple, but I now know where
>to change it, which is A Good Thing. Some comments at the start
>and at each section might be nice.
>
>RANDOM is a strange gForth-ism. Use CHOOSE (Brody). (Indeed, I
>crashed immediately and had to read the code).

It even runs on lina with some WANT's and I had to do
: RANDOM CHOOSE ;
too.

>
>I consider BYE a very bad word in Forth!


If you have to restart Forth and type
INCLUDE robotgame.frt
again, you would prefer a "Wanna play again".

I made it a turnkey, (leave out the last run, compile with -c)
then it is not so bad.

>
>Your program had 130 items on it after my first game. I think
>there should be a 2drop for the player or robot position somewhere.

I have the habit (with lina) of adding !CSP and ?CSP to the main
loops that must not grow the stack. If you make a mistake at some
lower level it is signaled immediately (error #20 STACK UNBALANCE)
It is available in gforth too. Like so:

: run reset-game
begin !CSP
move-robots board-print status-line cr help
#robots-alive 0= if ." You win!" cr bye then
any-collision? if
." You died! " cr bye
else user-input then
?CSP again ;

<SNIP>

>Congratulations!
+1

>
>-marcel
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Paul E. Bennett

unread,
Jul 19, 2013, 2:36:17ā€ÆPM7/19/13
to
Look on your first attempt as the prototype and use it to write a better
specification.

[%X]

> : #moves+ #moves 1+ to #moves ;
> : #moves- #moves 1- to #moves ;

Have a look at +! as a useful word that sometimes enables you to simplify
things a bit.

I would also suggest that on the next version you write what you expect each
word to do as a glossary style comment before you write the code. This is a
more Literal style of construction and helps in that the code becomes better
documented than just relying on the stack pictures and the word's name for a
clue. That way, when you look at the code in the future on you will have a
greater speed of understanding what you were trying to do.

--
********************************************************************
Paul E. Bennett...............<email://Paul_E....@topmail.co.uk>
Forth based HIDECS Consultancy
Mob: +44 (0)7811-639972
Tel: +44 (0)1235-510979
Going Forth Safely ..... EBA. www.electric-boat-association.org.uk..
********************************************************************

Stephen Pelc

unread,
Jul 20, 2013, 8:18:06ā€ÆAM7/20/13
to
On Fri, 19 Jul 2013 11:39:11 +0200, Christian Kellermann
<ck...@pestilenz.org> wrote:

>I have been re-reading my books on Forth lately and decided to get into
>learning it finally. One of the first results have been attached to this
>article and I would like to hear your criticism about it. (If this is a
>faux pas on this list please ignore this request).

I have just come back from a week teaching a Forth course, so your
post is very relevant to me.

>It's a small and silly implementation of the BSD game robots. You ("@")
>have to escape some robots ("R") and win by luring them into holes
>("*").

You had to explain this to us because the text above is not in the
code! All programmers, not just Forth programmers, fail to understand
that source code can have a very long life. After ten years, the
original programmer has probably left. After 30 years, nobody can
remember the job. Program for your successors.

>Things that did surprise me:
>
>* Working with the stack is not hard at all
>* Debugging Forth words has been easier than anticipated. I did not use
> the gforth debugging facilities though.
>* A bad choice of data structures is punished more heavily in forth due
> to unnecessary stack juggling involved than in other programming
> languages.

The stack just needs practice and a willingness to use the keyboard.

Debugging only requires you to know how formal scientific method
is done. See
http://www.mpeforth.com/arena/ProgramForth.pdf

>What I don't like in my program:
>
>* The word names are probably way too verbose for forthers. Comming from
> scheme I am quite accustomed to lengthy names and could not help it so
> far.
>* Redundant code due to my ignorance of the forth metaprogramming
> facilities. I need to read up on these.
>* Unclear when to use cells or bytes as an access unit for accessing
> buffers.
>* What's the preferred coding style for longer words? Or is this a sign
> that my word is too convoluted and should be refactored?
>* The access words for the values seem too verbose, are these even
> necessary?

What I find difficult in most Forth programs stems from them being
bottom up. If you scan them from the top, you don't know why/where
you're going. Hence, a description of each word is "very good thing".

Further, I emphasise that writing the stack comment and description
*before* you start coding the word will save time - your code will
have fewer stack faults and you will be coding to a specification.
Your successors and readers will thank you.

>Also I have the general feeling that it could be rewritten to take up
>less words. I probably choose a bad data representation.

That's just experience with a language. I've seen production
code that's a lot worse.

Stephen

--
Stephen Pelc, steph...@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads

Albert van der Horst

unread,
Jul 20, 2013, 8:36:42ā€ÆAM7/20/13
to
In article <b4tfg7...@mid.individual.net>,
I've insisted for a long time that if you do this correctly, you can
loose the ( x y i comments.) In the following parameters are marked with a
back quote.

In this case I'd have for update-robot:

\ Remember `x and `y as the new positon for robot `i, update the display.
: update-robot >r
floor-sym i robot@ board! \ Where robot was, is now floor
( x y ) i robot! \ new robot position
robot-sym i robot@ board! \ At new position we have robot
r> drop ;

update-robot was where the stack error was, by the way. I've rewritten it a bit,
such as to make it hard to remember where the stack error was.

P.S. For those playing I've the following cheat. You can't fall into a hole!
So you can win by teleporting until you're near a hole. Then sit on the hole
until all robots have falling into that hole.

>Paul E. Bennett...............<email://Paul_E....@topmail.co.uk>

Christian Kellermann

unread,
Jul 20, 2013, 3:24:07ā€ÆPM7/20/13
to
Hi Marcel,

thanks for your thoughts!

m.a.m....@tue.nl writes:
> RANDOM is a strange gForth-ism. Use CHOOSE (Brody). (Indeed, I
> crashed immediately and had to read the code).

I cannot find a reference for CHOOSE. I assume that you are not talking
about Leo Brodie, as I cannot find a reference to this word in my
edition Starting Forth Hardcover edition 1984, Forth Inc. nor Thinking
Forth (CC Print Edition 2004).

> I consider BYE a very bad word in Forth!

Yeah, I have had used QUIT in serveral instances first. BYE is a bit
rude indeed.

> Your program had 130 items on it after my first game. I think
> there should be a 2drop for the player or robot position somewhere.

Yes, I did not (or failed to) check this. The CSP(?) words mentioned in
other articles is a neat trick as a final safe guard against this kind
of error.

> Don't erase the screen, we have AT-XY in Forth.
> You can also use cursor keys with ANS Forth 20xx.

A right! I ran across page while seeing through editor words, but missed
AT-XY somehow.

> Maybe try >R and R>, it may simplify the stack some more.

right.

> It is not standard, but it helps to HIDE words which have no
> significance to the user (the maintainer also benefits).
> I use PRIVATE in iForth to do this.
>
> If you only want to write one game the source is OK. If you plan more,
> there seem to be quite a few words that could be in a separate
> library. In that case CREATE DOES> might be handy for boards,
> data structures etc. For now it is overkill.

All true.

Thank you!

Christian

Christian Kellermann

unread,
Jul 20, 2013, 3:28:59ā€ÆPM7/20/13
to
rickman <gnu...@gmail.com> writes:
> Another tenet of Forth programming is to thoroughly test your words
> before using them. It can be a lot of work to test each word
> separately, but this can save you lots of debug time chasing errors
> like forgetting to restore the return stack which will cause aborts
> and even crashes rather than just program errors.

This is actually how I wrote this. I jotted down the steps I would need
to do on paper, then implemented each of them as I went switching
between a running forth and my editor copying working words over. I
guess with "older" forths or forths providing a block editor this step
is blurred as you don't ever leave forth.

As pointed out by Stephen in another thread this should also have been
done as a comment in code to increase readability, maintainablility and
ease code review. All of these may be overkill for this game, but since
I asked for advice I should have done it alright.

Thanks!

Christian

Christian Kellermann

unread,
Jul 20, 2013, 3:31:32ā€ÆPM7/20/13
to
Hi Albert,

alb...@spenarnc.xs4all.nl (Albert van der Horst) writes:
> I have the habit (with lina) of adding !CSP and ?CSP to the main
> loops that must not grow the stack. If you make a mistake at some
> lower level it is signaled immediately (error #20 STACK UNBALANCE)
> It is available in gforth too. Like so:
>
> : run reset-game
> begin !CSP
> move-robots board-print status-line cr help
> #robots-alive 0= if ." You win!" cr bye then
> any-collision? if
> ." You died! " cr bye
> else user-input then
> ?CSP again ;

This is a very neat trick! This could also be used as a debugging help
by quickly bisecting chunks of words for a CSP mismatch.

Thanks for your other suggestions and kind encouragement,

Christian

P.S: What's lina?

Christian Kellermann

unread,
Jul 20, 2013, 3:34:44ā€ÆPM7/20/13
to
Hi Alex,
In the end I have decided not to use this, as I thought that since these
4 words are the only ones touching the memory layout of the robots
buffer. If I would ever change it these 4 words would also have to
change. And just changing any one of these constants by itself makes no
sense, so I "inlined" it in the words.

Your version is of course easier to read.

> For consistency with your other names, robot-alive should be
> robot-alive!

True!

Thanks for your suggestions!

Christian

Christian Kellermann

unread,
Jul 20, 2013, 3:41:59ā€ÆPM7/20/13
to
Warning: Uneducated personal opinions follow.

Wow, what a nice demo / game! I find the code very interesting! What
this example also shows me is that compared to "modern" HW, getting
graphics to work on the home computers has been straight forward (modulo
specific HW quirks) and less burdensome than today.

I may have 3d accelleration on this machine but I cannot use it without
understanding half a dozen interfaces, window systems, etc. Makes
computers even less accessible for "bare HW programming" which forth
shines in.

Thanks!

Christian

Christian Kellermann

unread,
Jul 20, 2013, 3:47:14ā€ÆPM7/20/13
to
"Elizabeth D. Rather" <era...@forth.com> writes:

> On 7/18/13 11:39 PM, Christian Kellermann wrote:
>> Dear group readers,
>>
>> I have been re-reading my books on Forth lately and decided to get into
>> learning it finally. One of the first results have been attached to this
>> article and I would like to hear your criticism about it. (If this is a
>> faux pas on this list please ignore this request).
>
> It's very good. I have seen a lot of more experienced Forth
> programmers write much uglier code.

Thank you!

>> * Redundant code due to my ignorance of the forth metaprogramming
>> facilities. I need to read up on these.
>
> Not sure what you mean. I don't see any instances of that sort.

The #teleports and #moves words for example are similar except for the
actual variable word.

>> * Unclear when to use cells or bytes as an access unit for accessing
>> buffers.
>
> Whichever is most convenient. I note you are frequently having to use
> "cells". If you container doesn't need to hold values >256, you can
> get away with bytes. I'm afraid I don't have enough time to analyze
> this more.

Also whether I mostly use aligned or unaligned access I guess.

> My biggest criticism is that you have some words without stack
> comments, and Marcel reports stuff is being left on the stack. When
> you are testing your code, it is essential that you check that every
> word conforms to its stack comment behavior, and that nothing is left
> on the stack that shouldn't be.

Yes, others also suggested this, even with some safe guards. As I have
written elsewhere in this thread I tested my words interactively but I
did not test them for their stack hygiene (If you can call it that). And
I imagine that this is a major blunder for any "real" application and
causes a lot of grief and sorrow, if not just in debugging time.

> Congratulations on a good start!

Thanks again!

Christian

Christian Kellermann

unread,
Jul 20, 2013, 3:48:57ā€ÆPM7/20/13
to
"Paul E. Bennett" <Paul_E....@topmail.co.uk> writes:
> Look on your first attempt as the prototype and use it to write a better
> specification.
>
> [%X]
>
>> : #moves+ #moves 1+ to #moves ;
>> : #moves- #moves 1- to #moves ;
>
> Have a look at +! as a useful word that sometimes enables you to simplify
> things a bit.

Indeed! Thanks for this hint!

> I would also suggest that on the next version you write what you expect each
> word to do as a glossary style comment before you write the code. This is a
> more Literal style of construction and helps in that the code becomes better
> documented than just relying on the stack pictures and the word's name for a
> clue. That way, when you look at the code in the future on you will have a
> greater speed of understanding what you were trying to do.

I agree! I did so on paper, but should have added that to the code as well.
It looks simple and easy to understand now, but I probably will have
forgotten all the details within a week.

Thanks!

Christian

Christian Kellermann

unread,
Jul 20, 2013, 3:53:49ā€ÆPM7/20/13
to
alb...@spenarnc.xs4all.nl (Albert van der Horst) writes:

> I've insisted for a long time that if you do this correctly, you can
> loose the ( x y i comments.) In the following parameters are marked with a
> back quote.
>
> In this case I'd have for update-robot:
>
> \ Remember `x and `y as the new positon for robot `i, update the display.
> : update-robot >r
> floor-sym i robot@ board! \ Where robot was, is now floor
> ( x y ) i robot! \ new robot position
> robot-sym i robot@ board! \ At new position we have robot
> r> drop ;
>
> update-robot was where the stack error was, by the way. I've rewritten
> it a bit, such as to make it hard to remember where the stack error
> was.

Ah I see the error now! Yes, having the comments aside from the code
does make it easy to follow. And the words are small enough to be easily
verifyable. From my experiences with (legacy) code written in other
languages I have a strong bias to distrust comments, especially the ones
on top of a function explaining what it does as these tend to bitrot
right after they have been written.

This of course does neither disqualify your remark nor the practise of
commenting code in forth or in general. I imagine you can also do
blunders as this with forth...

> P.S. For those playing I've the following cheat. You can't fall into a hole!
> So you can win by teleporting until you're near a hole. Then sit on the hole
> until all robots have falling into that hole.

Heh, neat one ;)

Cheers,

Christian


Christian Kellermann

unread,
Jul 20, 2013, 3:57:40ā€ÆPM7/20/13
to
Thanks for your comments! I actually wanted to rearrange the order of
the words a bit, but this is mostly limited to the interdependencies
between the words. Higher level words need lower level definitions, so
they need to be defined first.

Or am I just ignorant about my forth system's capabilities and it can to
a two (or multipass) when compiling words, so the order does not matter?
From the (old) literature I came to the opposite conclusion...

Kind regards,

Christian

Elizabeth D. Rather

unread,
Jul 20, 2013, 5:15:21ā€ÆPM7/20/13
to
On 7/20/13 9:47 AM, Christian Kellermann wrote:
> "Elizabeth D. Rather" <era...@forth.com> writes:
>
...
>>> * Redundant code due to my ignorance of the forth metaprogramming
>>> facilities. I need to read up on these.
>>
>> Not sure what you mean. I don't see any instances of that sort.
>
> The #teleports and #moves words for example are similar except for the
> actual variable word.

That actually brings up an interesting design issue. You have #moves and
#teleports defined as VALUES. If they were VARIABLES, you could just
say, e.g., -1 #moves +! which would hardly be worth defining as a word.

Choosing between a VALUE and a VARIABLE really doesn't matter a lot, but
VARIABLES are a slight win if you store into them roughly as much as you
fetch from them, while VALUEs are a slight win if most references are reads.

...
>> My biggest criticism is that you have some words without stack
>> comments, and Marcel reports stuff is being left on the stack. When
>> you are testing your code, it is essential that you check that every
>> word conforms to its stack comment behavior, and that nothing is left
>> on the stack that shouldn't be.
>
> Yes, others also suggested this, even with some safe guards. As I have
> written elsewhere in this thread I tested my words interactively but I
> did not test them for their stack hygiene (If you can call it that). And
> I imagine that this is a major blunder for any "real" application and
> causes a lot of grief and sorrow, if not just in debugging time.

*Always* check your stack after executing a word during testing! It's
essential that there be nothing left except its explicit results. Random
stuff left on the stack will bite you sooner or later.

Elizabeth D. Rather

unread,
Jul 20, 2013, 5:24:53ā€ÆPM7/20/13
to
On 7/20/13 9:57 AM, Christian Kellermann wrote:
> steph...@mpeforth.com (Stephen Pelc) writes:
>
,,,
>> What I find difficult in most Forth programs stems from them being
>> bottom up. If you scan them from the top, you don't know why/where
>> you're going. Hence, a description of each word is "very good thing".
>>
>> Further, I emphasise that writing the stack comment and description
>> *before* you start coding the word will save time - your code will
>> have fewer stack faults and you will be coding to a specification.
>> Your successors and readers will thank you.

Yes, and if your design changes be sure to update the comments, to avoid
that bit rot you mentioned elsewhere!

>>> Also I have the general feeling that it could be rewritten to take up
>>> less words. I probably choose a bad data representation.
>>
>> That's just experience with a language. I've seen production
>> code that's a lot worse.
>
> Thanks for your comments! I actually wanted to rearrange the order of
> the words a bit, but this is mostly limited to the interdependencies
> between the words. Higher level words need lower level definitions, so
> they need to be defined first.

Yep. You can design top-down, but you need to implement and test
bottom-up. It works fine, but as Stephen observes, it makes reading the
source top to bottom a little harder.

> Or am I just ignorant about my forth system's capabilities and it can to
> a two (or multipass) when compiling words, so the order does not matter?
> From the (old) literature I came to the opposite conclusion...

Forth implementations vary a lot nowadays! The indirect threaded code
model that is described in the early books is still around in some
systems, but others use subroutine-threaded code or optimized machine
code. However, the general rule that words must be defined before they
are used is still operative, and a good principle in "structured
programming" discipline, as well.

Coos Haak

unread,
Jul 20, 2013, 8:02:52ā€ÆPM7/20/13
to
Op 20 Jul 2013 12:36:42 GMT schreef Albert van der Horst:

> In article <b4tfg7...@mid.individual.net>,
<snip>
> I've insisted for a long time that if you do this correctly, you can
> loose the ( x y i comments.) In the following parameters are marked with a
> back quote.
>
> In this case I'd have for update-robot:
>
> \ Remember `x and `y as the new positon for robot `i, update the display.
>: update-robot >r
> floor-sym i robot@ board! \ Where robot was, is now floor
> ( x y ) i robot! \ new robot position
> robot-sym i robot@ board! \ At new position we have robot
> r> drop ;
>
As i is used to get the loop index and r@ to get a value on the return
stack, some implementations give a different result, so use r@ here to be
safe.
But other implementation give the same result, so there i equals r@ .
Look what `r@ i = .' displays: -1 or 0.

--
Coos

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

hughag...@yahoo.com

unread,
Jul 20, 2013, 11:02:00ā€ÆPM7/20/13
to
On Friday, July 19, 2013 2:39:11 AM UTC-7, Christian Kellermann wrote:
> Dear group readers,
>
> I have been re-reading my books on Forth lately and decided to get into
> learning it finally. One of the first results have been attached to this
> article and I would like to hear your criticism about it. (If this is a
> faux pas on this list please ignore this request).

Writing code and posting it is not a faux pas --- it is something that I would really like to see more of on comp.lang.forth!

I tell every novice the same thing --- use my novice package:
http://www.forth.org/novice.html
The whole point of the package is to make writing application programs easier. Your program would be a lot simpler and more straight-forward if you used my package.

> * Working with the stack is not hard at all
> * Debugging Forth words has been easier than anticipated. I did not use
> the gforth debugging facilities though.

I don't know what debugging facilities Gforth has, but I rarely take recourse in that kind of thing anyway --- I just write my words to have little or no side-effects, and then test them interactively --- that is the traditional Forth style.

> * A bad choice of data structures is punished more heavily in forth due
> to unnecessary stack juggling involved than in other programming
> languages.

The novice package has a FIELD word that allows you to define structs. This would simplify your program a lot, and reduce a lot of stack-juggling. For example, you could have a struct called COORDINATE that contains an X and Y coordinate --- passing around a pointer to the struct rather than the X and Y data itself, would halve the amount of data on your stack. Also, using structs tends to make the source-code more readable as it is more clear to the reader what the data is.

Forth isn't really lacking any features that "other programming languages" have, so there is no reason why Forth should be more punishing. You just have to know how to use Forth to take advantage of its features. :-)

> * The word names are probably way too verbose for forthers. Comming from
> scheme I am quite accustomed to lengthy names and could not help it so
> far.

That is not a problem at all. Scheme and Forth are pretty similar in regard to many style issues.

> * Redundant code due to my ignorance of the forth metaprogramming
> facilities. I need to read up on these.

This is the big problem with your program. You repeatedly define arrays manually. You need an array definer word. I don't recommend that novice Forthers mess around with meta-programming, writing defining words, and writing compile-time code. Just use one of the array definers in my novice package (see 2ARRAY for 2-dimensional arrays, or ARY for Iliff-vector arrays).

Get some experience writing application programs using the canned definers in the novice package first, and then later on you can take the next step up and learn how to write your own definers.

> * Unclear when to use cells or bytes as an access unit for accessing
> buffers.

I generally always use cells rather than bytes unless there are severe memory constraints --- using byte-size data is an optimization that shouldn't be considered unless absolutely necessary, as doing this tends to complicate the program (I'm guilty of this in my LowDraw program though, but I wrote that some 15 years ago at a time when I was somewhat out of practice at Forth).

> * What's the preferred coding style for longer words? Or is this a sign
> that my word is too convoluted and should be refactored?

Look at the code in my novice package for examples of style. In general, try not to mix ideas on the same line of source-code, and don't waste space either (such as by putting THEN on a line all by itself, which is common in Pascal with BEGIN and END). Avoid having excessively nested control structures, as is common in Pascal and C, and which I find to be very confusing. It is okay in Forth to use EXIT to get out of the middle of a control-structure --- there is no BREAK like in C, so this is often necessary --- this can actually help readability (there are many examples in the novice package).

Note that long words generally should be factored as much as possible.

In the novice package, I have a lot of words that take the xt of a colon word as a parameter --- they traverse a data-structure and apply that colon word to every node in the data-structure. This is a very effective way to factor programs --- by doing this, you can generally simplify your programs significantly.

> * The access words for the values seem too verbose, are these even
> necessary?

I don't like using values at all, and I much prefer to use only variables. The reasons why are somewhat advanced, and I don't think you are ready for that discussion --- this is just a word to the wise, for now.

If you do use values though, then there is help for you in my novice package. For example, I have +TO that would simplify your code.
For example:
> : #teleports+ #teleports 1+ to #teleports ;
Would become:
: #teleports+ 1 +to #teleports ;

Also, that is an awkward name. I would prefer: inc-#teleports
Actually, I would prefer that you didn't have these words at all. This is over-factoring --- they are not improving the readability of the code at all, and they just clutter up the source-code.

Some more tips:

1.) Don't initialize data-structures at compile-time. An example of this is:
> 3 value #teleports
Much better is to have an INIT word that initializes all of your data-structures at run-time. This helps a lot with debugging --- because you can INIT and get to a known-state, and you don't have to recompile the whole program every time.

2.) In the novice package I have a lot of support for random numbers. For example, I have randoms that are in a triangular distribution, rather than a rectangular distribution --- this can help games quite a lot.

> Also I have the general feeling that it could be rewritten to take up
> less words. I probably choose a bad data representation.

By far, the two biggest problems that I see, are these:
1.) You need to use FIELD to define structs.
2.) You need to use 2ARRAY or ARY to define your many arrays.

These two improvements will significantly shorten your program and make it more readable.

> What are your thoughts?

I think you did quite well! Don't let my suggestions above seem like criticism --- as a first effort, your program was quite good --- I've seen a lot worse from people who claim to be Forth experts, and you are essentially unteachable because they won't take advice. Because you are willing to learn, you are going to make fast progress --- and your background in Scheme already sets you way ahead of the majority of Forth novices (you know what meta-programming is, and you know about interactive debugging) --- so I expect that you are going to go far in Forth. :-)

hughag...@yahoo.com

unread,
Jul 20, 2013, 11:09:09ā€ÆPM7/20/13
to
On Friday, July 19, 2013 10:55:32 AM UTC-7, Elizabeth D. Rather wrote:
> On 7/18/13 11:39 PM, Christian Kellermann wrote:
> > * Redundant code due to my ignorance of the forth metaprogramming
> > facilities. I need to read up on these.
>
> Not sure what you mean. I don't see any instances of that sort.

This was the most glaring problem with his program --- he is manually defining arrays in several places --- the program would be much simplified if it used an array definer.

Since you are a teacher of a novice Forth class at Forth Inc., you should have noticed this immediately, and you should have been able to show him how to write an array definer.

Elizabeth D. Rather

unread,
Jul 20, 2013, 11:18:05ā€ÆPM7/20/13
to
You should never, ever use I except inside a DO ... LOOP structure to
get the index. There is no reason not to use R@, because that is clear
as to what you're doing (in addition to being correct on all systems).

Albert van der Horst

unread,
Jul 20, 2013, 11:36:45ā€ÆPM7/20/13
to
In article <15255f3b-1fc8-45b9...@googlegroups.com>,
<hughag...@yahoo.com> wrote:
>On Friday, July 19, 2013 2:39:11 AM UTC-7, Christian Kellermann wrote:
>> Dear group readers,
>>
>> I have been re-reading my books on Forth lately and decided to get into
>> learning it finally. One of the first results have been attached to this
>> article and I would like to hear your criticism about it. (If this is a
>> faux pas on this list please ignore this request).
>
>Writing code and posting it is not a faux pas --- it is something that I
>would really like to see more of on comp.lang.forth!
>
>I tell every novice the same thing --- use my novice package:
>http://www.forth.org/novice.html
>The whole point of the package is to make writing application programs
>easier. Your program would be a lot simpler and more straight-forward if
>you used my package.
>

What about posting some real code. Rewrite the robotgame using
the novice package and show us how much advantages that has.

Groetjes Albert

rickman

unread,
Jul 20, 2013, 11:41:36ā€ÆPM7/20/13
to
On 7/20/2013 11:02 PM, hughag...@yahoo.com wrote:
> On Friday, July 19, 2013 2:39:11 AM UTC-7, Christian Kellermann wrote:
>> Dear group readers,
>>
>> I have been re-reading my books on Forth lately and decided to get into
>> learning it finally. One of the first results have been attached to this
>> article and I would like to hear your criticism about it. (If this is a
>> faux pas on this list please ignore this request).
>
> Writing code and posting it is not a faux pas --- it is something that I would really like to see more of on comp.lang.forth!
>
> I tell every novice the same thing --- use my novice package:
> http://www.forth.org/novice.html
> The whole point of the package is to make writing application programs easier. Your program would be a lot simpler and more straight-forward if you used my package.

Can you give specific examples of how his program would be "simpler and
more straight-forward" if he used your novice package? Maybe this is
something I should check out. I've been writing Forth for a number of
years, but only in small spurts so I often forget much of what I have
learned, continually being a novice, lol.

As I read further down I see where you do give specifics...


>> * Working with the stack is not hard at all
>> * Debugging Forth words has been easier than anticipated. I did not use
>> the gforth debugging facilities though.
>
> I don't know what debugging facilities Gforth has, but I rarely take recourse in that kind of thing anyway --- I just write my words to have little or no side-effects, and then test them interactively --- that is the traditional Forth style.

There are times when I would not mind having a debugger or simulator
that would let me single step through a given word definition viewing
the stack. There are times when the error is in a misunderstanding of
what stack effect a word has. A single step debugger would make this
visible a lot quicker. But then this is not a mistake a more
experienced Forth programmer makes very often. More of a novice crutch.


>> * A bad choice of data structures is punished more heavily in forth due
>> to unnecessary stack juggling involved than in other programming
>> languages.
>
> The novice package has a FIELD word that allows you to define structs. This would simplify your program a lot, and reduce a lot of stack-juggling. For example, you could have a struct called COORDINATE that contains an X and Y coordinate --- passing around a pointer to the struct rather than the X and Y data itself, would halve the amount of data on your stack. Also, using structs tends to make the source-code more readable as it is more clear to the reader what the data is.

That sounds useful.


> Forth isn't really lacking any features that "other programming languages" have, so there is no reason why Forth should be more punishing. You just have to know how to use Forth to take advantage of its features. :-)

I suppose that is a valid point.


>> * The word names are probably way too verbose for forthers. Comming from
>> scheme I am quite accustomed to lengthy names and could not help it so
>> far.
>
> That is not a problem at all. Scheme and Forth are pretty similar in regard to many style issues.
>
>> * Redundant code due to my ignorance of the forth metaprogramming
>> facilities. I need to read up on these.
>
> This is the big problem with your program. You repeatedly define arrays manually. You need an array definer word. I don't recommend that novice Forthers mess around with meta-programming, writing defining words, and writing compile-time code. Just use one of the array definers in my novice package (see 2ARRAY for 2-dimensional arrays, or ARY for Iliff-vector arrays).
>
> Get some experience writing application programs using the canned definers in the novice package first, and then later on you can take the next step up and learn how to write your own definers.

I think this is where I would disagree with you. I think that by
defining his own arrays, he learns where an issue exists. Now he is
motivated to learn how to code up array defining words and this is a
perfect time to take that step. The program is working and now he can
improve the code by learning about defining words.


>> * Unclear when to use cells or bytes as an access unit for accessing
>> buffers.
>
> I generally always use cells rather than bytes unless there are severe memory constraints --- using byte-size data is an optimization that shouldn't be considered unless absolutely necessary, as doing this tends to complicate the program (I'm guilty of this in my LowDraw program though, but I wrote that some 15 years ago at a time when I was somewhat out of practice at Forth).

I agree on that one. It is pretty generally accepted that optimizations
should be done once the code is written and the small portion of the
design that is ripe for optimization can be identified and dealt with.
Some call it the 90/10 rule others call it the 80/20 rule (80% of the
gain can be had optimizing 20% of the code).

--

Rick

hughag...@yahoo.com

unread,
Jul 21, 2013, 1:28:27ā€ÆAM7/21/13
to
On Saturday, July 20, 2013 8:36:45 PM UTC-7, Albert van der Horst wrote:
> In article <15255f3b-1fc8-45b9...@googlegroups.com>,
> <hughag...@yahoo.com> wrote:
> >I tell every novice the same thing --- use my novice package:
> >http://www.forth.org/novice.html
> >The whole point of the package is to make writing application programs
> >easier. Your program would be a lot simpler and more straight-forward if
> >you used my package.
>
> What about posting some real code. Rewrite the robotgame using
> the novice package and show us how much advantages that has.

I have written abundant example code, but nobody has looked at it. I don't actually learn very well by looking at example code either --- people only learn by doing --- so I leave it up to him to use the novice package to simplify his program.

Albert van der Horst

unread,
Jul 21, 2013, 6:31:23ā€ÆAM7/21/13
to
In article <18debd42-bf40-44ee...@googlegroups.com>,
You expect us to look at your code, but you refuse to look at someone
else's code. You leave it up to a novice to come up with a convincing
example of your novice package.

The excuses are becoming a bit too lame. My conclusion is that your
novice code is not worth a damn.

Christian Kellermann

unread,
Jul 22, 2013, 9:53:40ā€ÆAM7/22/13
to
steph...@mpeforth.com (Stephen Pelc) writes:

> On Fri, 19 Jul 2013 11:39:11 +0200, Christian Kellermann
> <ck...@pestilenz.org> wrote:
>
>>I have been re-reading my books on Forth lately and decided to get into
>>learning it finally. One of the first results have been attached to this
>>article and I would like to hear your criticism about it. (If this is a
>>faux pas on this list please ignore this request).
>
> I have just come back from a week teaching a Forth course, so your
> post is very relevant to me.
>
>
> What I find difficult in most Forth programs stems from them being
> bottom up. If you scan them from the top, you don't know why/where
> you're going. Hence, a description of each word is "very good thing".
>
> Further, I emphasise that writing the stack comment and description
> *before* you start coding the word will save time - your code will
> have fewer stack faults and you will be coding to a specification.
> Your successors and readers will thank you.

I have updated the program according to your and suggestion by the other
fine people in this group, please find the new version below.

I have added comments and restructured it a little bit, also you now
cannot run into holes and the number of robots increases with each
completed level.

Thanks!

Christian

--8<---------------cut here---------------start------------->8---
\ Robots.fth - An implementation of the BSD robots game
\
\ You ("@") are placed into a wide space covered with holes ("o") and
\ robots ("R"). Try to lure the robots into the holes to win. If a
\ robot catches you, you loose. You are able to teleport yourself to a
\ random location 3 times.

require random.fs

\ statistics
variable #moves 0 #moves !
variable #teleports 3 #teleports !

\ the board
20 constant board-rows
78 constant board-cols

: random-xy ( -- x y ) \ returns random x y coordinates
board-cols random board-rows random ;

board-rows board-cols * constant board-dimension
create board board-dimension allot
: xy->board ( x y -- i ) \ converts xy coordinates to the board buffer index
board-cols * + ;
: board@ ( x y -- c ) \ fetches a tile at coordinates x y
xy->board board + c@ ;
: board! ( c x y -- ) \ stores tile char c at coordinates x y
xy->board board + c! ;

\ drawing words
char @ constant player-sym
char R constant robot-sym
char o constant hole-sym
32 constant floor-sym \ a space character
char | constant wall-sym

: border ( -- ) \ draw a border like '+----+' for the board
[char] + emit
board-cols 0 do [char] - emit loop
[char] + emit ;

: wall ( -- ) \ print a wall symbol
wall-sym emit ;

: board-reset ( -- ) \ empties a board by placing floor tiles in it
board-dimension 0 do \ for the whole board
floor-sym board i + ! \ store a floor tile
loop ;

: board-print ( -- ) \ prints the whole board
\ clears screen, prints the board surrounded
\ by walls and a border on top and bottom
page border cr wall
board-dimension 0 do
i board-cols mod 0= i 0> and if \ special case for first position at (0,0)
wall cr wall then \ otherwise draw a wall at the end of each line
board i + @ emit \ and draw the (next) tile
loop
wall cr border cr ;

\ player
create (player) 2 cells allot

: player@ ( -- x y ) \ returns current player x y position
(player) 2@ ;

: player! ( x y -- ) \ sets current player position to x y
(player) 2! ;

: new-position ( x1 y1 dx dy -- x2 y2 ) \ adds an offset to the current position
rot + \ calculate y2 ( x1 dx y2 )
>r \ store y2
+ r> ; \ add x1 dx and push y2

: valid-position? ( x y -- f ) \ checks whether the player can be placed here
2dup 2>r \ save coordinates for later
0 board-rows within swap \ y is within board boundaries
( b x ) 0 board-cols within and \ x is also within boundaries?
2r> ( x y ) board@ floor-sym = and ; \ is it a free space? This prevents falling into holes

: up ( -- dx dy ) 0 -1 ;
: down ( -- dx dy ) 0 1 ;
: left ( -- dx dy ) -1 0 ;
: right ( -- dx dy ) 1 0 ;

: teleport-location ( -- ) \ ensure that the new location is legal
random-xy \ get random coordinates
2dup valid-position? invert \ use a copy for testing, is valid?
if 2drop \ if it is not, clean stack
recurse then ; \ and try again

: update-player ( x y -- )
\ move the player tile on the board
\ and update the player position
floor-sym player@ board! \ set the floor tile at old pos
player-sym rot rot \ ( player-sym x y )
2dup player! \ set player position, leave x y
( player-sym x y ) board! ; \ set player tile at new position

: move ( dx dy -- )
\ high level movement word, taking a direction
\ and moving the player if the new position is valid
player@ 2swap new-position \ get new x y coordinates
2dup valid-position? \ if these are valid
if update-player \ move to it
else 2drop then ; \ or restore the stack

\ hole words
10 constant #holes
create holes #holes 2* cells allot
: hole@ ( i -- x y ) 2* cells holes + 2@ ;
: hole! ( x y i -- ) 2* cells holes + 2! ;


\ robot words
\ we do have a maximum of 20 robots
20 constant #max-robots
\ we start with 2 robots
2 value #robots
\ robots occupy 3 cells: x y alive?
create robots #max-robots 3 * cells allot

: robot@ ( i -- x y ) \ return the x y position of the ith robot
3 * cells robots + 2@ ;

: robot! ( x y i -- ) \ set the ith robot's postion to x y
3 * cells robots + 2! ;

: robot-alive! ( t i -- ) \ set the alive flag to f for the ith robot
3 * cells 2 cells + robots + ! ;
: robot-alive? ( i -- t ) \ return the alive flag for the ith robot
3 * cells 2 cells + robots + @ ;

: #robots-alive ( -- n ) \ returns the numer of robots that are alive
0 \ always return something sensible
#robots 0 ?do \ we iterate over the current number of robots
i robot-alive? \ it it is alive
if 1+ then \ count it
loop ;

: update-robot ( x y i -- ) \ sets robot position and board tile
>r \ save index for later usage
floor-sym r@ robot@ board! \ replace the old place with floor tile
r@ robot! \ update the robot
robot-sym r@ robot@ board! \ robot tie on new pos
r> drop ; \ clean stack

: distance ( x1 y1 x2 y2 -- x2-x1 y2-y1) \ returns the distance between two positions
>r \ save y2
rot - \ x2-x1
r> rot - \ y2-y1
swap ; \ -> x' y'

: sign ( n1 -- n2 ) \ calculate the sign of a number
dup 0=
if
drop 0 \ 0 -> 0
else
dup abs / \ n1 / (abs n1) -> -1 / 1
then ;

: direction ( d1 d2 -- dx dy ) \ extract the direction from a given position
sign swap sign ;

: towards-player ( x y -- x' y' ) \ return new position that moves one step toward the player's current pos
2dup player@ distance direction new-position ;

: collision? ( x1 y1 x2 y2 -- f ) \ are the two positions the same?
xy->board \ get the index for the second position
rot rot \ move first position to TOS
xy->board = ; \ convert to index and compare

: is-in-hole? ( x y -- f ) \ checks whether a given position is on a hole
xy->board \ convert to index
false \ initialise with false
#holes 0 do
over \ get a copy of the index
i hole@ xy->board \ is it on this hole?
= or \ yes? if so or it
loop swap drop ; \ ( i f ) -> ( -- f )

: move-robot ( i -- ) \ move the i-th robot towards the player
dup >r robot@ towards-player \ get the new coords
2dup is-in-hole? \ fell into a hole
if false r@ robot-alive! \ yes, this is a dead robot
floor-sym r> robot@ board! \ replace the old robot position with an empty tile
2drop \ remove the wrong position again
else
r> update-robot \ if alive update the robot's position and the board
then ;

: move-robots ( -- ) \ highlevel word to move all robots towards player
#robots 0 ?do
i robot-alive? \ if robot i is alive
if i move-robot then loop ; \ move it towards player

: any-collision? ( -- f ) \ returns true if any robot caught the player
false \ we assume that the player is well
#robots 0 \ for all robots
?do i robot-alive? \ is this robot alive
if i robot@ player@ collision? \ has this robot caught the player?
or \ set flag to true if so
then loop ;

\ game routines init, loop
: init-robots ( -- ) \ place the current number of robots on the board and activate them
#robots 0 ?do
robot-sym random-xy 2dup i robot! board! \ place them on board and initialise position
true i robot-alive! loop ; \ switch them on

: init-holes ( -- ) \ randomly scatter holes on the board
#holes 0 do hole-sym random-xy 2dup i hole! board! loop ;

: init-player ( -- ) \ place player on the board
teleport-location \ find a spot where the player can live
2dup player! update-player \ update board tiles and player pos
0 #moves ! \ reset move counter
3 #teleports ! ; \ give the player 3 teleports

: reset-game ( -- ) \ sets up a new game with the current number of robots
board-reset init-player init-robots init-holes ;
: status-line ( -- ) \ prints a status line on screen
." moves: " #moves @ . ." teleports: " #teleports @ . ." robots: " #robots-alive . ;
: help ( -- ) \ prints a key legend on screen
." h: left, j: down, k: up, l: right, t: teleport, q: quit, any other key waits." cr ;

: user-input ( -- ) \ waits for one key, then handles player movement
key
case
[char] h of left move endof
[char] j of down move endof
[char] k of up move endof
[char] l of right move endof
[char] q of ." Thanks for playing! " quit endof
[char] t of #teleports @ 0>
if #teleports @ 1- #teleports !
teleport-location 2dup
update-player player! then endof
endcase
1 #moves +! ;

: run ( -- ) \ main loop and entry point
!csp
2 to #robots \ start with 2 robots
reset-game
begin
board-print status-line cr help \ print the board
#robots-alive 0= \ are robots left?
if ." You win!" cr \ no, next round with more robots
#max-robots #robots 1+ min to #robots \ unless there are already #max-robots
reset-game then
any-collision? \ did they catch the player?
if ." You died! " cr quit \ yes, player dies
else user-input then \ handle user input
move-robots \ robots move last
?csp \ watch out for an unclean stack
again ;

here seed ! \ initialise PRNG
run \ start the game
--8<---------------cut here---------------end--------------->8---

m.a.m....@tue.nl

unread,
Jul 22, 2013, 11:06:49ā€ÆAM7/22/13
to
On Monday, July 22, 2013 3:53:40 PM UTC+2, Christian Kellermann wrote:
> : help ( -- ) \ prints a key legend on screen
> ." h: left, j: down, k: up, l: right, t: teleport, q: quit, any other key
> waits." cr ;

This is Wiggins-style commenting :-)
Feel free to ignore advice that is obviously wrong.

-marcel

Anton Ertl

unread,
Jul 22, 2013, 11:32:02ā€ÆAM7/22/13
to
Christian Kellermann <ck...@necronomicon.my.domain> writes:
>> I consider BYE a very bad word in Forth!
>
>Yeah, I have had used QUIT in serveral instances first. BYE is a bit
>rude indeed.

QUIT is just as bad. EXIT would be appropriate in RUN, maybe preceded
by some cleanup. USER-INPUT is also close enough to the top that you
don't need to resort to THROW.

- 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 2013: http://www.euroforth.org/ef13/

Anton Ertl

unread,
Jul 22, 2013, 11:36:40ā€ÆAM7/22/13
to
Christian Kellermann <ck...@pestilenz.org> writes:
>* Debugging Forth words has been easier than anticipated. I did not use
> the gforth debugging facilities though.

Good!

>* The access words for the values seem too verbose, are these even
> necessary?

Not a bad solution, but I would have factored "3 * cells robots +"
out. Or actually I would have used structure words and the accesses
would have looked differently.

Anton Ertl

unread,
Jul 22, 2013, 11:43:05ā€ÆAM7/22/13
to
Christian Kellermann <ck...@necronomicon.my.domain> writes:
>rickman <gnu...@gmail.com> writes:
>> Another tenet of Forth programming is to thoroughly test your words
>> before using them. It can be a lot of work to test each word
>> separately, but this can save you lots of debug time chasing errors
>> like forgetting to restore the return stack which will cause aborts
>> and even crashes rather than just program errors.
>
>This is actually how I wrote this. I jotted down the steps I would need
>to do on paper, then implemented each of them as I went switching
>between a running forth and my editor copying working words over.

The way I usually work is to write the Forth words and the test code
in the editor and then run

gforth code.fs -e bye

whenever I feel like testing. You can do it for every word if you want.

Albert van der Horst

unread,
Jul 22, 2013, 3:35:06ā€ÆPM7/22/13
to
In article <3b6db8e4-8af7-4589...@googlegroups.com>,
I don't agree.
I would have it this way:

\ prints a key legend on screen
: help
." h: left, j: down, k: up, l: right, t: teleport, q: quit, any other key
waits." cr ;

Then my help would show " prints ..screen" if I asked for help
and the code generator would extract a glossary entry:

help ( -- )
Prints a key legend on screen

For a word like this a specification is maybe more essential then
for others. This "help" function consists totally of side effects, i.e.
output to the screen. In general it is even more important to document
side effects than to document normal functions.
The latter could be guessed from the name and is incorporated into the
function by which is it called.

Furthermore if you had to guess what it does from the name, you could've
guessed anything, like information about what a good game strategy is.

Imagine this would've been an assignment:

5 Add a help function

would you approve of
: help ;

or would you expect this to mean
5A Find a reasonable specification for a help function
5B Now implement it

>-marcel

m.a.m....@tue.nl

unread,
Jul 23, 2013, 3:41:17ā€ÆAM7/23/13
to
On Monday, July 22, 2013 9:35:06 PM UTC+2, Albert van der Horst wrote:
>>This is Wiggins-style commenting :-)
>>Feel free to ignore advice that is obviously wrong.

> I don't agree.

"Wiggins-style comments" repeat what the code aleady says in slightly
different words, without adding other insights. These comments are
sure to be wrong after a few iterations (not so bad because nobody
will read them).

> I would have it this way:

No, you are describing a mechanism for automatic glossary
generation, which is *very different* from generating good
documentation. LOCATE beats a glossary every time. In fact, my
current belief is that LOCATE is an absolutely essential
part of Forth.

iForth has machinery to show help for each included
module. The help contains the user visible words and a small
usage example.

-marcel

Christian Kellermann

unread,
Jul 23, 2013, 6:00:41ā€ÆAM7/23/13
to
alb...@spenarnc.xs4all.nl (Albert van der Horst) writes:

> In article <3b6db8e4-8af7-4589...@googlegroups.com>,
> <m.a.m....@tue.nl> wrote:
>>On Monday, July 22, 2013 3:53:40 PM UTC+2, Christian Kellermann wrote:
>>
>>This is Wiggins-style commenting :-)
>>Feel free to ignore advice that is obviously wrong.
>
> I don't agree.
> I would have it this way:
>
> \ prints a key legend on screen
> : help
> ." h: left, j: down, k: up, l: right, t: teleport, q: quit, any other key
> waits." cr ;
>
> Then my help would show " prints ..screen" if I asked for help
> and the code generator would extract a glossary entry:
>
> help ( -- )
> Prints a key legend on screen

Is this glossary generation a common feature of forth systems? If so
which is the way to get this?

Thanks,

Christian

Lars Brinkhoff

unread,
Jul 23, 2013, 6:22:39ā€ÆAM7/23/13
to
No one said anything about spacing and indentation. Which is probably
good, as it's less important than many other considerations.

But anyway, are Leo Brodie's guidlines out of style?

http://www.forth.org/forth_style2.html

Christian Kellermann

unread,
Jul 23, 2013, 7:44:04ā€ÆAM7/23/13
to
It's a good guideline I guess. Just hard to remember everything at once
while learning something new :)

Thanks for pointing it out to me!

Christian

Albert van der Horst

unread,
Jul 23, 2013, 8:10:53ā€ÆAM7/23/13
to
In article <87d2q9e...@pestilenz.org>,
It was more of an example, and you could imagine a slightly sophisticated
LOCATE that does something equivalent.
This said, quite some of the professional Forth systems have a feature
like that. I think it is a great asset to have to be able to promote
Forth in the work environment.

I invite the likes of MPE to speak for themselves.

A mature (if not vintage) 16-bit Forth like chforth has a feature to extract
comment lines that start with /D.

This is a quote from gforth\s glosgen.fs :
"
: \G postpone \ ; immediate
\G \G is an alias for \, so it is a comment till end-of-line, but
\G it has a special meaning for the Glossary Generator.
"

>
>Thanks,
>
>Christian

Christian Kellermann

unread,
Jul 23, 2013, 8:19:16ā€ÆAM7/23/13
to
alb...@spenarnc.xs4all.nl (Albert van der Horst) writes:
> It was more of an example, and you could imagine a slightly sophisticated
> LOCATE that does something equivalent.
> This said, quite some of the professional Forth systems have a feature
> like that. I think it is a great asset to have to be able to promote
> Forth in the work environment.
>
> I invite the likes of MPE to speak for themselves.
>
> A mature (if not vintage) 16-bit Forth like chforth has a feature to extract
> comment lines that start with /D.
>
> This is a quote from gforth\s glosgen.fs :
> "
> : \G postpone \ ; immediate
> \G \G is an alias for \, so it is a comment till end-of-line, but
> \G it has a special meaning for the Glossary Generator.
> "

I do see the benefit of this especially in larger programs, where
accessing the glossary of each word quickly would help understanding
parts of a program. Like an online help in a (block) editor.

Albert van der Horst

unread,
Jul 23, 2013, 8:26:25ā€ÆAM7/23/13
to
In article <87siz5c...@pestilenz.org>,
Christian Kellermann <ck...@pestilenz.org> wrote:
>Lars Brinkhoff <lars...@nocrew.org> writes:
>
>> No one said anything about spacing and indentation. Which is probably
>> good, as it's less important than many other considerations.
>>
>> But anyway, are Leo Brodie's guidlines out of style?
>>
>> http://www.forth.org/forth_style2.html
>
>It's a good guideline I guess. Just hard to remember everything at once
>while learning something new :)

You should keep in mind that F83 is block-based, i.e. all code is kept
in 16 lines of 64 characters. The spec is about horizontal style that
is all but mandatory for blocks.

If using text files, you can use more white space without loosing the
overview. And you can use longer lines, but above 80 char's lines
become hard to read.
The alternative is called vertical style.
Its main feature is that opening and closing parts of a structure
line up vertically, as in BEGIN .. REPEAT DO .. LOOP : .. ;

The most important remark of Brody is:
"
Whatever you choose, be consistent.
"

>
>Thanks for pointing it out to me!
>
>Christian

Anton Ertl

unread,
Jul 23, 2013, 9:44:23ā€ÆAM7/23/13
to
Christian Kellermann <ck...@pestilenz.org> writes:
>Is this glossary generation a common feature of forth systems? If so
>which is the way to get this?

MPE markets its Docgen (IIRC) as a separate feature.

In Gforth it is just used internally for generating the Gforth
documentation, and no documentation for using it exists; and you would
have to separate the commands used from Gforth's Makefile and put them
into a separate script.

Elizabeth D. Rather

unread,
Jul 23, 2013, 1:10:42ā€ÆPM7/23/13
to
There are similar style guidelines in my books (Forth Programmer's
Handbook, available as a pdf with all our systems including the free
eval versions and in paperback from Amazon and Forth Application
Techniques, a tutorial for ANS Forth available from Amazon).

The most common stack notation nowadays is a little less terse than
Brodie shows, for example using addr for an address and addr len for an
address-length pair representing a string.

It's important, IMO, for programmers working in a group to agree on
common style guidelines and stick to them. That makes it a lot easier to
share code.

Ian Osgood

unread,
Jul 23, 2013, 7:07:36ā€ÆPM7/23/13
to
On Saturday, July 20, 2013 5:36:42 AM UTC-7, Albert van der Horst wrote:
> : update-robot >r
> floor-sym i robot@ board! \ Where robot was, is now floor
> ( x y ) i robot! \ new robot position
^^^^^^^^^
> robot-sym i robot@ board! \ At new position we have robot
> r> drop ;

Isaac Asimov would approve of this refactoring!

(Nice first program, Christian! Better than my first one.)

Ian

Stephen Pelc

unread,
Jul 24, 2013, 5:27:18ā€ÆAM7/24/13
to
On 23 Jul 2013 12:10:53 GMT, alb...@spenarnc.xs4all.nl (Albert van der
Horst) wrote:

>>Is this glossary generation a common feature of forth systems? If so
>>which is the way to get this?
>
>It was more of an example, and you could imagine a slightly sophisticated
>LOCATE that does something equivalent.
>This said, quite some of the professional Forth systems have a feature
>like that. I think it is a great asset to have to be able to promote
>Forth in the work environment.
>
>I invite the likes of MPE to speak for themselves.

MPE's Forths have had LOCATE <name> for a very long time to get
to the source code. We also have HELP <name> in the desktop
Forths to get to the right page of the PDF manual.

We have used DocGen for 15+ years now to generate all our new manuals,
including the VFX Forths. From the same Forth source, DocGen can
generate HTML or PDF documents. For PDFs, a LaTeX installation is
required.

>This is a quote from gforth\s glosgen.fs :
>"
> : \G postpone \ ; immediate
> \G \G is an alias for \, so it is a comment till end-of-line, but
> \G it has a special meaning for the Glossary Generator.
>"

VFX Forth uses a different notation, e.g.

: CloseDoc \ --
\ *G Close the current document.
...
;

Comments that start at (say) the left hand edge and start
\ *x
are treated as instructions to the DocGen documentation generator.
This avoids having to invent new words such as \G and allows
documentation to be added to systems, e.g. source for embedded
code, that do not themselves support DocGen.

An interesting observation is that the use of DocGen makes our
code better. Whenever we receive third party code for inclusion
in our products, we always DocGen the code; and we always find
bugs while doing this.

DocGen is fully documented in itself and appears in all versions
of VFX Forth. If imitation is the sincerest form of flattery, an
old version of DocGen has been cloned in another Forth.

Stephen

--
Stephen Pelc, steph...@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads

Stephen Pelc

unread,
Jul 24, 2013, 5:30:46ā€ÆAM7/24/13
to
On Tue, 23 Jul 2013 12:22:39 +0200, Lars Brinkhoff
<lars...@nocrew.org> wrote:

>But anyway, are Leo Brodie's guidlines out of style?
>
>http://www.forth.org/forth_style2.html

Those are designed for blocks, which are mostly obsolete
for source code.

Documentation is discussed in some depth in my book at
http://www.mpeforth.com/arena/ProgramForth.pdf

The key is that you should be consistent and use the same
house coding style as you colleagues.

hughag...@yahoo.com

unread,
Jul 24, 2013, 11:00:42ā€ÆPM7/24/13
to
On Monday, July 22, 2013 8:43:05 AM UTC-7, Anton Ertl wrote:
> Christian Kellermann <ck...@necronomicon.my.domain> writes:
> >rickman <gnu...@gmail.com> writes:
> >> Another tenet of Forth programming is to thoroughly test your words
> >> before using them. It can be a lot of work to test each word
> >> separately, but this can save you lots of debug time chasing errors
> >> like forgetting to restore the return stack which will cause aborts
> >> and even crashes rather than just program errors.
>
> >This is actually how I wrote this. I jotted down the steps I would need
> >to do on paper, then implemented each of them as I went switching
> >between a running forth and my editor copying working words over.

> The way I usually work is to write the Forth words and the test code
> in the editor and then run
>
> gforth code.fs -e bye
>
> whenever I feel like testing. You can do it for every word if you want.

In my novice package I have a word TRY. You are expected to put a MARKER at the front of your file, with the name of the file. For example, if your file is called ROBOT.4TH then put this at the front of the file:

MARKER ROBOT.4TH

You can do this interactively:

TRY ROBOT.4TH

If ROBOT.4TH is already included, the code gets deleted. In either case, ROBOT.4TH is given to INCLUDE at this time. This works a lot better than manually doing this:
INCLUDE ROBOT.4TH
Doing this would result in a lot of redefinition warnings, which is quite annoying --- also, you will eventually run out of memory.

As I said before, you should not initialize data-structures when defining them. You should have a word INIT that initializes everything. If you don't want to do a TRY and recompile the code (because you haven't changed the source-code), you can just do an INIT and run it again.

Christian Kellermann

unread,
Jul 25, 2013, 4:37:02ā€ÆAM7/25/13
to
hughag...@yahoo.com writes:
> As I said before, you should not initialize data-structures when
> defining them. You should have a word INIT that initializes
> everything. If you don't want to do a TRY and recompile the code
> (because you haven't changed the source-code), you can just do an INIT
> and run it again.

Which data-structures are you referring to? The buffers? These are not
meant to be resizeable once the program runs. The other magic values in
my code that aren't set by words are constants and necessary to
determine the memory the buffers need to hold.

And for the rest I do have initialisation words, the just have different
names than INIT. I think INIT is not a good name per se as it is very
unspecific and tends to grow into a grab bag where several unrelated
things tend to end up. A bit like a "miscellaneous" labelled box on your
desk.

Kind regards,

Christian

hughag...@yahoo.com

unread,
Jul 26, 2013, 3:00:20ā€ÆAM7/26/13
to
On Thursday, July 25, 2013 1:37:02 AM UTC-7, Christian Kellermann wrote:
> hughag...@yahoo.com writes:
> > As I said before, you should not initialize data-structures when
> > defining them. You should have a word INIT that initializes
> > everything. If you don't want to do a TRY and recompile the code
> > (because you haven't changed the source-code), you can just do an INIT
> > and run it again.
>
> Which data-structures are you referring to? The buffers? These are not
> meant to be resizeable once the program runs. The other magic values in
> my code that aren't set by words are constants and necessary to
> determine the memory the buffers need to hold.

I was referring to code like this:

variable #moves 0 #moves !
variable #teleports 3 #teleports !

2 value #robots

You want to have a word like this:

: init
0 #moves !
3 #teleports !
2 to #robots
;

INIT gets your program back to a pristine state. That way you can test it from a known configuration.

As your program is currently written, you have to recompile to get back to a known state. Compilation in Forth is quite fast, so this isn't a terribly bad thing, and with TRY it is convenient. Still though, it is a bad design in which you to have to recompile every time that the program is run.

This bit about INIT is a fairly minor point. I still think that the best way to simplify your program is like this:

1.) Use structs to reduce the amount of data that you have on the stack. For example, a struct for a point that contains X and Y coordinates, rather than putting the X and Y data directly on the stack.

2.) Use an array definer. You currently implement several arrays manually, which tends to clutter up your source-code. You don't want low-level code, such as array access, intermingled with your application code --- factoring out the low-level code helps make your application code shorter and easier to read.

If you don't yet know how to implement structs or arrays, or definer words in general, then you can use what I have provided in the novice package. The point of the novice package is to allow people to get started on writing programs, without requiring them to first implement a lot of low-level support code. Later on you will likely develop your own package of low-level code, but that is not something that you want to get bogged down in right now.

I have example programs in the novice package that you may want to study and perhaps build on. For example, I wrote a program to solve the N-Queens puzzle. I put all of the solutions in lists. The purpose of this, was so that I could write a program that would examine the solutions and delete the duplicates (mirror-images and rotations). I never wrote the program to weed out the duplicates though --- so that is left as an exercise for the reader --- if you or anybody else writes it, I will put it in the next novice-package release.

Keep at it! Your Forth code is quite good for a first effort. :-)

Christian Kellermann

unread,
Jul 26, 2013, 3:34:20ā€ÆAM7/26/13
to
Ok, I am still a bit confused about this though. How does INIT differ
from RESET-GAME and/or INIT-PLAYER which sets these? Arguably #robots
only gets reset in RUN but this has been quite convenient when testingā€¦


> This bit about INIT is a fairly minor point. I still think that the
> best way to simplify your program is like this:
>
> 1.) Use structs to reduce the amount of data that you have on the
> stack. For example, a struct for a point that contains X and Y
> coordinates, rather than putting the X and Y data directly on the
> stack.
>
> 2.) Use an array definer. You currently implement several arrays
> manually, which tends to clutter up your source-code. You don't want
> low-level code, such as array access, intermingled with your
> application code --- factoring out the low-level code helps make your
> application code shorter and easier to read.

For larger code bases this may be true but I think this is
overdoing it for this little program. Also while a REQUIRE or INCLUDE
from a library makes this program shorter to read it becomes more
difficult to understand as I would have to read the included source code
part first, as people already had to do with the RANDOM word I used.

In general I tend to agree though, abstraction is goodā€¦

> If you don't yet know how to implement structs or arrays, or definer
> words in general, then you can use what I have provided in the novice
> package. The point of the novice package is to allow people to get
> started on writing programs, without requiring them to first implement
> a lot of low-level support code. Later on you will likely develop your
> own package of low-level code, but that is not something that you want
> to get bogged down in right now.

ā€¦but I like to implement my own abstractions which I fully know and
understand. To abstract things you first have to know the inner workings
then you can forget about them for a while. But I guess know I am
preaching to the choir.

> Keep at it! Your Forth code is quite good for a first effort. :-)

Thanks for your supportive comments!

Christian

Elizabeth D. Rather

unread,
Jul 26, 2013, 3:40:37ā€ÆAM7/26/13
to
On 7/25/13 9:34 PM, Christian Kellermann wrote:
> hughag...@yahoo.com writes:
...
>> I was referring to code like this:
>>
>> variable #moves 0 #moves !
>> variable #teleports 3 #teleports !
>>
>> 2 value #robots
>>
>> You want to have a word like this:
>>
>> : init
>> 0 #moves !
>> 3 #teleports !
>> 2 to #robots
>> ;
>>
>> INIT gets your program back to a pristine state. That way you can test
>> it from a known configuration.
>>
>> As your program is currently written, you have to recompile to get
>> back to a known state. Compilation in Forth is quite fast, so this
>> isn't a terribly bad thing, and with TRY it is convenient. Still
>> though, it is a bad design in which you to have to recompile every
>> time that the program is run.
>
> Ok, I am still a bit confused about this though. How does INIT differ
> from RESET-GAME and/or INIT-PLAYER which sets these? Arguably #robots
> only gets reset in RUN but this has been quite convenient when testingā€¦

Your init words were fine. Hugh probably didn't read far enough to see them.

>> This bit about INIT is a fairly minor point. I still think that the
>> best way to simplify your program is like this:
>>
>> 1.) Use structs to reduce the amount of data that you have on the
>> stack. For example, a struct for a point that contains X and Y
>> coordinates, rather than putting the X and Y data directly on the
>> stack.
>>
>> 2.) Use an array definer. You currently implement several arrays
>> manually, which tends to clutter up your source-code. You don't want
>> low-level code, such as array access, intermingled with your
>> application code --- factoring out the low-level code helps make your
>> application code shorter and easier to read.
>
> For larger code bases this may be true but I think this is
> overdoing it for this little program. Also while a REQUIRE or INCLUDE
> from a library makes this program shorter to read it becomes more
> difficult to understand as I would have to read the included source code
> part first, as people already had to do with the RANDOM word I used.
>
> In general I tend to agree though, abstraction is goodā€¦

I agree with you. This is a simple program, and only needs simple data
structures.

>> If you don't yet know how to implement structs or arrays, or definer
>> words in general, then you can use what I have provided in the novice
>> package. The point of the novice package is to allow people to get
>> started on writing programs, without requiring them to first implement
>> a lot of low-level support code. Later on you will likely develop your
>> own package of low-level code, but that is not something that you want
>> to get bogged down in right now.
>
> ā€¦but I like to implement my own abstractions which I fully know and
> understand. To abstract things you first have to know the inner workings
> then you can forget about them for a while. But I guess know I am
> preaching to the choir.

Very good practice.

hughag...@yahoo.com

unread,
Jul 27, 2013, 11:16:14ā€ÆPM7/27/13
to
On Friday, July 26, 2013 12:40:37 AM UTC-7, Elizabeth D. Rather wrote:
> On 7/25/13 9:34 PM, Christian Kellermann wrote:
> > hughag...@yahoo.com writes:
> > Ok, I am still a bit confused about this though. How does INIT differ
> > from RESET-GAME and/or INIT-PLAYER which sets these? Arguably #robots
> > only gets reset in RUN but this has been quite convenient when testingā€¦
>
> Your init words were fine. Hugh probably didn't read far enough to see them.

You are duplicating code. What if your boss walks into your office and says: "Using my brilliant leadership skills, I have determined that the game should allow 4 teleports rather than 3." --- now you have to make the change in two places rather than just one.

It is best to have one word, call it whatever you want, that initializes everything to a known configuration.

Anyway, like I said, this is a minor point.

> >> I still think that the
> >> best way to simplify your program is like this:
>
> >> 1.) Use structs to reduce the amount of data that you have on the
> >> stack. For example, a struct for a point that contains X and Y
> >> coordinates, rather than putting the X and Y data directly on the
> >> stack.
>
> >> 2.) Use an array definer. You currently implement several arrays
> >> manually, which tends to clutter up your source-code. You don't want
> >> low-level code, such as array access, intermingled with your
> >> application code --- factoring out the low-level code helps make your
> >> application code shorter and easier to read.
>
> > For larger code bases this may be true but I think this is
> > overdoing it for this little program. Also while a REQUIRE or INCLUDE
> > from a library makes this program shorter to read it becomes more
> > difficult to understand as I would have to read the included source code
> > part first, as people already had to do with the RANDOM word I used.
>
> > In general I tend to agree though, abstraction is goodā€¦
>
> I agree with you. This is a simple program, and only needs simple data
> structures.

What is good for large programs is also good for small programs. With small programs you can fudge it and still get your program to work, but if you make a habit of this, you will plateau at a fairly basic level and never graduate to writing large programs. Also, remember the maxim that I quoted previously: "Large programs that work started out as small programs that worked." Most large programs started out as small programs and then they evolved from there, but this only works if the small programs were written using the same good practices that are required for large programs.

Also, I'm recommending that you use structs and array-definers. These are simple data-structures.

Even if I was recommending that you use associations or something else complicated, if that were the appropriate data-structure, then you should use it --- the complication is under the hood, so using it doesn't complicate your program at all. My associations are pretty complicated internally, but the interface is quite simple (only slightly more complicated than the interface to the linked lists, as there are a few more features).

> >> If you don't yet know how to implement structs or arrays, or definer
> >> words in general, then you can use what I have provided in the novice
> >> package. The point of the novice package is to allow people to get
> >> started on writing programs, without requiring them to first implement
> >> a lot of low-level support code. Later on you will likely develop your
> >> own package of low-level code, but that is not something that you want
> >> to get bogged down in right now.
>
> > ā€¦but I like to implement my own abstractions which I fully know and
> > understand. To abstract things you first have to know the inner workings
> > then you can forget about them for a while. But I guess know I am
> > preaching to the choir.
>
> Very good practice.

That would be a very good practice, if he had actually done it. LOL

Don't get bogged down in implementing low-level support code like this when you are just starting out. First, get your feet wet by writing application programs. The purpose of the novice package is to let you do this. You can work your way up from simple programs like this to pretty large programs, just using the novice package. Later on, when you have some experience, then you can write your own package comparable to the novice package --- but you aren't going to be able to write code comparable to the novice package without several years of experience --- so don't rush it.

Note that Elizabeth Rather and the entire Forth Inc. staff have NEVER written anything comparable to the novice package. None of those books from Forth Inc. mention structs at all, and structs are pretty basic. If you spend upwards of $500 on SwiftForth, you don't get any support for writing applications --- you are still exactly where you are right now, wondering how to abstract out data-structures --- or, worse, ignoring the problem and just manually coding this low-level stuff (arrays!) inside of your application programs, as you did in this robot-game program.

Just to get an idea of how badly you have painted yourself into a corner, try this exercise: upgrade the program so that the size of the datum stored in the arrays is different from what it is now (add a color datum associated with each board position). Or upgrade it so there are more than one kind of robot that behave differently (some that actively chase you, and some that wander around aimlessly). Or upgrade it so there are more than one kind of hole (some shallow, so the robot can climb out in a few clock ticks). Because you have hard-wired so much information into your code, minor upgrades such as this will be horrendous to implement --- this is a small program that will NEVER evolve into a large program!

As a second exercise, rewrite the program from scratch the way that I have described --- then pretend that your boss has walked into your office and demanded the upgrades described above, which you had not expected --- you will see that the upgrades are much easier to implement when the foundation is solid.

The world is full of bad Forth programmers --- don't be one of them! --- get into the habit of writing EVERY program in a professional manner, whether it is small or large. :-)

Christian Kellermann

unread,
Jul 29, 2013, 4:09:30ā€ÆAM7/29/13
to
hughag...@yahoo.com writes:
>
> What is good for large programs is also good for small programs. With
> small programs you can fudge it and still get your program to work,
> but if you make a habit of this, you will plateau at a fairly basic
> level and never graduate to writing large programs. Also, remember the
> maxim that I quoted previously: "Large programs that work started out
> as small programs that worked." Most large programs started out as
> small programs and then they evolved from there, but this only works
> if the small programs were written using the same good practices that
> are required for large programs.

I disagree with you here. Let me add my own anecdotal claim: The large
programs I have seen so far are most of the time in a constant state of
rewrite. This is always due to changing specs and requirements from the
users. Noone is able to foresee all possible requirements from the
start.

On the other hand programs written with all possible changes in mind
tend to get more complicated than they have to be and one finds
programmes just adding another layer of indirection around the first one
they couldn't fully understand.

> Also, I'm recommending that you use structs and array-definers. These
> are simple data-structures.

The data-structures aren't complicated, neither arrays, nor associated
lists nor skip lists nor anything else.

> That would be a very good practice, if he had actually done it. LOL

What I did was writing a program that fits the *current* specification
(which has been made up by myself very conveniently!) with as little
effort as possible. What I got is a small and readable program, even if
it is awkward in some placed because I didn't use common forth words (BL
comes to mind, or BLANK for the initialisation of the board).

> Don't get bogged down in implementing low-level support code like this
> when you are just starting out. First, get your feet wet by writing
> application programs. The purpose of the novice package is to let you
> do this. You can work your way up from simple programs like this to
> pretty large programs, just using the novice package. Later on, when
> you have some experience, then you can write your own package
> comparable to the novice package --- but you aren't going to be able
> to write code comparable to the novice package without several years
> of experience --- so don't rush it.

I like building things from the ground upwards. If I need to know how
other, more experienced people write more complicated data structures I
can look at their code and learn or ask someone who knows. c.l.f is one
of the few nice places on usenet for these kind of discussions.

> Note that Elizabeth Rather and the entire Forth Inc. staff have NEVER
> written anything comparable to the novice package. None of those books
> from Forth Inc. mention structs at all, and structs are pretty
> basic.

I cannot speak for them. I personally find the books available nice as
an introductory text and that's what their goal is. I am half way
through the "Forth Programmers Handbook" and it has been a nice
companion to the "classic" texts like "Starting FORTH" and "Thinking
FORTH". Also Stephen's Program Forth book seems also great, it's next on
my book stack.

> If you spend upwards of $500 on SwiftForth, you don't get any
> support for writing applications --- you are still exactly where you
> are right now, wondering how to abstract out data-structures --- or,
> worse, ignoring the problem and just manually coding this low-level
> stuff (arrays!) inside of your application programs, as you did in
> this robot-game program.

I don't see why this is a Bad Thing at all.

> Just to get an idea of how badly you have painted yourself into a
> corner, try this exercise: upgrade the program so that the size of the
> datum stored in the arrays is different from what it is now (add a
> color datum associated with each board position). Or upgrade it so
> there are more than one kind of robot that behave differently (some
> that actively chase you, and some that wander around aimlessly). Or
> upgrade it so there are more than one kind of hole (some shallow, so
> the robot can climb out in a few clock ticks). Because you have
> hard-wired so much information into your code, minor upgrades such as
> this will be horrendous to implement --- this is a small program that
> will NEVER evolve into a large program!

As I said above, this is a change in spec that requires change in the
program. And yes, the assumption that I store just a byte for each board
position may become hard to maintain in the future. Then *this* part of
the program needs to be replaced with a better data structure.

> As a second exercise, rewrite the program from scratch the way that I
> have described --- then pretend that your boss has walked into your
> office and demanded the upgrades described above, which you had not
> expected --- you will see that the upgrades are much easier to
> implement when the foundation is solid.

Let's pretend I projected a budget for the robots game with these specs,
then the customer changes her mind about it. Then I would have gained
speed if and *only* if I anticipated that mind change in the *right* way.

If I would make a wrong guess, for my boss this would have been wasted
money. On the other hand if I guessed right I would have been the hero
of the day as the change could happen quickly.

I claim that this is almost impossible to get right and all effort
invested in implementing features based on guesses what the future
requirements *might* be like is vasted effort.

> The world is full of bad Forth programmers --- don't be one of them!
> --- get into the habit of writing EVERY program in a professional
> manner, whether it is small or large. :-)

Now, I would like to say that if I am to implement your changes above, I
would write yet another robot program reusing words maybe, but with a
different data structure underneath.

And as you said after a while I would see which words are worth keeping
and are truly reusable.

I did have a look at the novice package and it has been an interesting
read! I still think for me this is -- at least for the moment -- to much
to deal with.

Thanks for your thoughts,

Christian

m.a.m....@tue.nl

unread,
Jul 29, 2013, 7:14:19ā€ÆAM7/29/13
to
On Monday, July 29, 2013 10:09:30 AM UTC+2, Christian Kellermann wrote:
[..]
I like your approach of building only and exactly what you need.
It is not often that we see a Forth novice approach it in such a
clear-headed way.

-marcel

Andrew Haley

unread,
Jul 29, 2013, 8:49:47ā€ÆAM7/29/13
to
I agree. It's a real pleasure to watch.

Andrew.

Elizabeth D. Rather

unread,
Jul 29, 2013, 9:32:16ā€ÆPM7/29/13
to
I entirely agree.

Christian Kellermann

unread,
Jul 31, 2013, 7:23:21ā€ÆAM7/31/13
to

A quick follow up to the statement below:

hughag...@yahoo.com writes:
> Note that Elizabeth Rather and the entire Forth Inc. staff have NEVER
> written anything comparable to the novice package. None of those books
> from Forth Inc. mention structs at all, and structs are pretty
> basic. If you spend upwards of $500 on SwiftForth, you don't get any
> support for writing applications --- you are still exactly where you
> are right now, wondering how to abstract out data-structures --- or,
> worse, ignoring the problem and just manually coding this low-level
> stuff (arrays!) inside of your application programs, as you did in
> this robot-game program.

Now that I have read the Forth Programmers Handbook, I agree those are
not covered there. But the book points the reader to Stephen Pelc's
Programming Forth, where structured data is explained nicely in chapters
8 and 9 onwards, where CREATE and DOES> gets introduced. (Forth
Programmers Handbook does cover arrays but not in detail.). Also a
trivial but easy to understand example is shown involving creating a
phone book and inserting/querying data to/from it.

While these may be trivial they have helped me to understand DOES> and I
feel confident to define my own defining words when I have to.

My thanks go to the authors of these books.

Christian

hughag...@yahoo.com

unread,
Jul 31, 2013, 8:50:06ā€ÆPM7/31/13
to
On Monday, July 29, 2013 1:09:30 AM UTC-7, Christian Kellermann wrote:
> Let's pretend I projected a budget for the robots game with these specs,
> then the customer changes her mind about it. Then I would have gained
> speed if and *only* if I anticipated that mind change in the *right* way.
>
> If I would make a wrong guess, for my boss this would have been wasted
> money. On the other hand if I guessed right I would have been the hero
> of the day as the change could happen quickly.
>
> I claim that this is almost impossible to get right and all effort
> invested in implementing features based on guesses what the future
> requirements *might* be like is vasted effort.

You make it sound as if I'm requiring you to use a crystal ball to predict the future changes, and that any wrong guess on your part will result in a huge waste of time. Actually, predicting the future is not all that difficult --- it is possible to predict with 100% accuracy for EVERY program, and you don't need a crystal ball.

Here is the prediction that always comes true:
Your program involves a certain amount of data in each node, and there will be MORE data in the future.

This is why structs are important. The node is defined as a struct containing some fields. Later on, new fields will need to be added to the struct (the prediction has come true!). Because you have contained the struct definition in one place however, adding new fields is easy to do. What you have right now, is your "struct" hard-wired into your program all over the place --- adding new fields involves changing the program all over the place --- this is what I described previously as "horrendous."

Define the nodes as a struct, and you can easily add new fields. Also, define words that deal with the nodes to take as parameters pointers to nodes, rather than raw data (such as X and Y), so they don't have to be changed if new fields are added. For example, if you upgrade from 2-dimensional to 3-dimensional, and so you now have X, Y and Z, you don't have to change the program so that it now deals with three data rather than two data all over the place, but just in a few functions.

You don't have to predict what new fields will be required in the future, as that could be almost anything --- you just have to predict that there will be new fields required in the future.

This is not a complicated idea, as C programmers have always used structs for this purpose --- C programmers aren't all that smart, yet they succeed wonderfully in isolating change with this technique. To a large extent, what causes a Forth program to turn into a gigantic mess, is that struct definition wasn't isolated. When new data was required in each node, tweaks in the code were needed all over the place, which resulted in a lot of error-prone work for the programmer as the change wasn't isolated at all, but could be needed almost anywhere in the program.

Believe me --- this is professional programming. When I wrote MFX, the assembler, simulator and Forth cross-compiler for the MiniForth processor, the processor's definition was changing on an almost daily basis --- I had to write the program in such a way that I could cope with this moving target. Tying yourself to a particular set of data is a pitfall that you won't climb out of, but which will result in total failure.

hughag...@yahoo.com

unread,
Jul 31, 2013, 9:05:37ā€ÆPM7/31/13
to
On Wednesday, July 31, 2013 4:23:21 AM UTC-7, Christian Kellermann wrote:
> While these may be trivial they have helped me to understand DOES> and I
> feel confident to define my own defining words when I have to.

I don't use CREATE DOES> at all --- I consider the use of CREATE DOES> to be much too novice-level to be used in my novice package. These are the reasons:

1.) CREATE DOES> is inherently inefficient because it involves storing constant data (with comma after the CREATE) that is known at compile-time. It is inefficient to fetch this data out of memory at run-time and work with it as if it were mutable data, which it isn't. Pelc has a non-standard hack in VFX that allows the programmer to tell the compiler that this data is immutable --- by comparison, my novice package solves this problem while yet conforming to ANS-Forth, so the user isn't tied to any particular commercial compiler but can use any ANS-Forth-compliant compiler.

2.) CREATE DOES> only allows for ONE action associated with the data. It is possible to associate other actions using colon words with >BODY to get to the data after the CREATE, but this is pretty clunky, and it is not consistent with the way that the DOES> action works.

All in all, I'm not too impressed with your confidence in knowing how to write definer words, when all you know is CREATE DOES>, which I consider to be an obsolete 1970s-vintage aspect of Forth --- the universe of what can be done in Forth is a somewhat larger than you realize! :-)

Elizabeth D. Rather

unread,
Aug 1, 2013, 2:04:04ā€ÆAM8/1/13
to
There is more coverage of DOES> in Forth Application Techniques.

Christian Kellermann

unread,
Aug 1, 2013, 6:53:29ā€ÆAM8/1/13
to
hughag...@yahoo.com writes:

> On Wednesday, July 31, 2013 4:23:21 AM UTC-7, Christian Kellermann wrote:
>> While these may be trivial they have helped me to understand DOES> and I
>> feel confident to define my own defining words when I have to.
>
> I don't use CREATE DOES> at all --- I consider the use of CREATE DOES>
> to be much too novice-level to be used in my novice package. These are
> the reasons:
>
> 1.) CREATE DOES> is inherently inefficient because it involves storing
> constant data (with comma after the CREATE) that is known at
> compile-time. It is inefficient to fetch this data out of memory at
> run-time and work with it as if it were mutable data, which it
> isn't. Pelc has a non-standard hack in VFX that allows the programmer
> to tell the compiler that this data is immutable --- by comparison, my
> novice package solves this problem while yet conforming to ANS-Forth,
> so the user isn't tied to any particular commercial compiler but can
> use any ANS-Forth-compliant compiler.

Whenever you write novice package here, you really are talking about
:NAME and its compangions right?

> All in all, I'm not too impressed with your confidence in knowing how
> to write definer words, when all you know is CREATE DOES>, which I
> consider to be an obsolete 1970s-vintage aspect of Forth --- the
> universe of what can be done in Forth is a somewhat larger than you
> realize! :-)

I don't assume that I know everything, I know for certain that I
don't. From what I have read so far is that the way current
implementations deal with flexibility now is vectored execution. If I
understood the source of novice.4th correctly, this is what you are
doing as well.

Elizabeth D. Rather

unread,
Aug 1, 2013, 10:58:15ā€ÆAM8/1/13
to
On 8/1/13 5:53 AM, Christian Kellermann wrote:
...
>
> I don't assume that I know everything, I know for certain that I
> don't. From what I have read so far is that the way current
> implementations deal with flexibility now is vectored execution. If I
> understood the source of novice.4th correctly, this is what you are
> doing as well.
>

It's a little unclear exactly what you mean by "flexibility". Vectored
execution is certainly useful, for providing different behaviors for
words such as TYPE, which can potentially direct its output to a variety
of different devices. It is not a substitute for forward references
except in a few cases.

I am glad you're exploring the use of CREATE ... DOES> as it's one of
the most powerful features in Forth. In a competent implementation it's
quite efficient.

hughag...@yahoo.com

unread,
Aug 1, 2013, 11:42:53ā€ÆPM8/1/13
to
On Thursday, August 1, 2013 3:53:29 AM UTC-7, Christian Kellermann wrote:
> Whenever you write novice package here, you really are talking about
> :NAME and its compangions right?

When I write "novice package," I mean the whole package. :NAME is an important part of the novice package --- this is what I use rather than CREATE DOES>.

> I don't assume that I know everything, I know for certain that I
> don't. From what I have read so far is that the way current
> implementations deal with flexibility now is vectored execution. If I
> understood the source of novice.4th correctly, this is what you are
> doing as well.

Current implementations? What programs are you referring to? Different programmers have different style, and some may not use vectored execution very much.

There are two kinds of factoring:

1.) You break big functions down into several small functions. So the "factored out" functions are the low-level aspects of the original overblown function.

2.) You have a function that takes an xt as a parameter. This function typically traverses a data-structure and calls the passed-in function (gives the xt to EXECUTE) for every node in the data-structure. So the "factored out" function is the high-level aspect of the original overblown function.

The best example of the #2 technique, is the EACH word in LIST.4TH. Instead of using cut-and-paste to insert the code for traversing a list wherever it is needed, I have factored out the code for traversing a list as the function EACH. This function can be used no matter what it is that you want to do to each node in the list.

In regard to EACH, the code for traversing a list is pretty short and simple and could easily be cut-and-pasted throughout the application program. Still though, using EACH simplifies the application program, especially if list-traversal is very common (as it is in the slide-rule program). Also, this technique works well with more complicated data-structures (such as the association, which is an LLRB tree) in which it is not realistic to cut-and-paste because of the size and complexity of the traversal code.

EACH is a little more complicated that you might expect. It holds its internal data on the return-stack rather than the parameter-stack during the EXECUTE of the passed-in function. The purpose of this, is to expose the parameter-stack to the passed-in function. It can access data that is on the stack to pass information back to the creator function. This is a crude kind of closure --- it is the best that I could do while still being ANS-Forth compliant --- it works reasonably well. In my language that I'm writing, I will have true closures --- the passed-in function will be able to access the local variables of the creator function --- but, until then, you have the novice package with its many work-arounds for ANS-Forth limitations.

Brad Eckert

unread,
Aug 4, 2013, 9:53:09ā€ÆPM8/4/13
to
On Wednesday, July 31, 2013 9:05:37 PM UTC-4, hughag...@yahoo.com wrote:
> All in all, I'm not too impressed with your confidence in knowing how to write definer words, when all you know is CREATE DOES>, which I consider to be an obsolete 1970s-vintage aspect of Forth

You mean discovered and hashed out when every clock cycle mattered.

There's a little overhead there, but the simplicity is worth it. Complexity's hidden costs are universally underestimated.

I looked in your "novice package" again (thank goodness 2012 came and went without me having to machine a slide rule to replace my EMP-fried computer) but I don't see how :NAME addresses the inefficiencies of CREATE DOES>. If DOES> worked well when chip features were measured in tens of microns, how bad can it be now?

Elizabeth D. Rather

unread,
Aug 4, 2013, 11:05:19ā€ÆPM8/4/13
to
It would be interesting to see some actual timing comparisons of
CREATE...DOES> vs. :NAME.

hughag...@yahoo.com

unread,
Aug 4, 2013, 11:54:24ā€ÆPM8/4/13
to
On Sunday, August 4, 2013 8:05:19 PM UTC-7, Elizabeth D. Rather wrote:
> On 8/4/13 8:53 PM, Brad Eckert wrote:
> > On Wednesday, July 31, 2013 9:05:37 PM UTC-4, hughag...@yahoo.com wrote:
> >> All in all, I'm not too impressed with your confidence in knowing how to write definer words, when all you know is CREATE DOES>, which I consider to be an obsolete 1970s-vintage aspect of Forth
>
> > You mean discovered and hashed out when every clock cycle mattered.
>
> > There's a little overhead there, but the simplicity is worth it. Complexity's hidden costs are universally underestimated.

What??? This is just blather --- you're not saying anything.

> > I looked in your "novice package" again (thank goodness 2012 came and went without me having to machine a slide rule to replace my EMP-fried computer) but I don't see how :NAME addresses the inefficiencies of CREATE DOES>. If DOES> worked well when chip features were measured in tens of microns, how bad can it be now?
>
> It would be interesting to see some actual timing comparisons of
> CREATE...DOES> vs. :NAME.

The only thing that prevents this comparison from being done, is that you've never posted any code on comp.lang.forth or provided any code in any of your books that compares to anything in the novice package.

Write some code comparable to 2ARRAY and do the test under SwiftForth.

Note that regurgitating the array definer in "Starting Forth" won't do, because it doesn't have the features that 2ARRAY does --- you'll have to actually write some code yourself, for the first time ever.

Alex McDonald

unread,
Aug 5, 2013, 8:50:54ā€ÆAM8/5/13
to
On Monday, 5 August 2013 04:05:19 UTC+1, Elizabeth D. Rather wrote:

>
> It would be interesting to see some actual timing comparisons of
> CREATE...DOES> vs. :NAME.
>
>
> Cheers,
>
> Elizabeth
>
>

Provided by Hugh please! He has the habit of pronouncing total victory in the war on performance without there being any evidence that he's taken part in even a small skirmish.

Or, as one might say in Forth;

: aguilar create does> ;

Christian Kellermann

unread,
Aug 5, 2013, 4:14:57ā€ÆPM8/5/13
to
hughag...@yahoo.com writes:

> On Wednesday, July 31, 2013 4:23:21 AM UTC-7, Christian Kellermann wrote:
>> While these may be trivial they have helped me to understand DOES> and I
>> feel confident to define my own defining words when I have to.
>
> I don't use CREATE DOES> at all --- I consider the use of CREATE DOES>
> to be much too novice-level to be used in my novice package. These are
> the reasons:
>
> 1.) CREATE DOES> is inherently inefficient because it involves storing
> constant data (with comma after the CREATE) that is known at
> compile-time. It is inefficient to fetch this data out of memory at
> run-time and work with it as if it were mutable data, which it
> isn't. Pelc has a non-standard hack in VFX that allows the programmer
> to tell the compiler that this data is immutable --- by comparison, my
> novice package solves this problem while yet conforming to ANS-Forth,
> so the user isn't tied to any particular commercial compiler but can
> use any ANS-Forth-compliant compiler.

But is tied to the novice packageā€¦

Your package would probably help people if it would work like a grab bag
for words to reuse in their own programs but the novice package words
are intermingled with each other so it *looks* like an all or nothing
move.

> All in all, I'm not too impressed with your confidence in knowing how
> to write definer words, when all you know is CREATE DOES>, which I
> consider to be an obsolete 1970s-vintage aspect of Forth --- the
> universe of what can be done in Forth is a somewhat larger than you
> realize! :-)

It helps me most if I understand a problem based on my current mental
model. I haven't seen any sign of obsolescence for CREATE and DOES> yet
apart from your opinion.

Also other language communities usually tell the newbies not to worry
about performance for their programs until it really matters. Replacing
something simple with a complex implementation of a similar interface,
requiring me to understand and learn that as well through the argument
of performance is not convincing me at this point.

hughag...@yahoo.com

unread,
Aug 7, 2013, 12:11:36ā€ÆAM8/7/13
to
On Monday, August 5, 2013 1:14:57 PM UTC-7, Christian Kellermann wrote:
> hughag...@yahoo.com writes:
>
> > I don't use CREATE DOES> at all --- I consider the use of CREATE DOES>
> > to be much too novice-level to be used in my novice package. These are
> > the reasons:
>
> > 1.) CREATE DOES> is inherently inefficient because it involves storing
> > constant data (with comma after the CREATE) that is known at
> > compile-time. It is inefficient to fetch this data out of memory at
> > run-time and work with it as if it were mutable data, which it
> > isn't.

> Also other language communities usually tell the newbies not to worry
> about performance for their programs until it really matters. Replacing
> something simple with a complex implementation of a similar interface,
> requiring me to understand and learn that as well through the argument
> of performance is not convincing me at this point.

I listed two reasons --- you edited out #2 and addressed only #1.

Since you are a freshly minted expert on CREATE DOES>, write a version of ARY using CREATE DOES>. This has already been tried by a big comp.lang.forth expert with years of experience, but his definer was limited to arrays of cells --- it did not support arrays of records of any size as my ARY does. Of course, the usual suspects all said that arrays of records aren't necessary and that the user should be required to always have arrays of pointers to records (the same argument used against my SORT that supports both arrays of records and arrays of pointers to records).

This challenge is possible --- I could do it --- but the solution would be complicated to implement and cumbersome to use (and inefficient, although you don't care about that).

I think that it is somewhat early for you to say that CREATE DOES> is simpler than :NAME --- get some experience writing defining words first.

hughag...@yahoo.com

unread,
Aug 17, 2013, 8:06:54ā€ÆPM8/17/13
to
Well, a lot of time has passed and you haven't written an array definer yet. So, go ahead and regurgitate the array definer from "Starting Forth." It is hard-wired for cell sized records, but whatever --- use it if that is all you have.

hughag...@yahoo.com

unread,
Aug 17, 2013, 8:45:07ā€ÆPM8/17/13
to
On Monday, August 5, 2013 5:50:54 AM UTC-7, Alex McDonald wrote:
> On Monday, 5 August 2013 04:05:19 UTC+1, Elizabeth D. Rather wrote:
>
> > It would be interesting to see some actual timing comparisons of
> > CREATE...DOES> vs. :NAME.
>
> Provided by Hugh please! He has the habit of pronouncing total victory in the war on performance without there being any evidence that he's taken part in even a small skirmish.

When I write the CREATE DOES> definer to be compared against my novice package definer, then everybody says that I sabotaged the CREATE DOES> code to be inefficient, and that a CREATE DOES> expert would do so much better --- but, of course, they don't provide any code themselves.

I've already been through this, showing a comparison of my FIELD with the typical CREATE DOES> version of FIELD.

From this thread (dated 12/02/09):
https://groups.google.com/forum/#!searchin/comp.lang.forth/$3Aname$20field$20create$20does%3E/comp.lang.forth/TTvONOrHvI4/X63CXcZYaPgJ
We have this:

My package also includes the word FIELD. Most Forth programmers would
define FIELD like this:

: field ( offset size -- new-offset )
create
over , +
does> ( record -- field-adr )
@ + ;

It could be used like this:

0
w field .aaa
w field .bbb
constant rrr

create sss rrr allot

: ttt ( record -- aaa+bbb )
dup .aaa @ swap .bbb @ + ;

Using SwiftForth, the CREATE-DOES> version of FIELD generates this
code:

see field
4756FF 40DDBF ( CREATE ) CALL E8BB86F9FF
475704 4 # EBP SUB 83ED04
475707 EBX 0 [EBP] MOV 895D00
47570A 4 [EBP] EBX MOV 8B5D04
47570D 40828F ( , ) CALL E87D2BF9FF
475712 0 [EBP] EBX ADD 035D00
475715 4 # EBP ADD 83C504
475718 40C2CF ( (;CODE) ) CALL E8B26BF9FF
47571D 4 # EBP SUB 83ED04 .AAA and .BBB call this
475720 EBX 0 [EBP] MOV 895D00
475723 EBX POP 5B EBX now the base-adr
475724 0 [EBX] EBX MOV 8B1B
475726 0 [EBP] EBX ADD 035D00
475729 4 # EBP ADD 83C504
47572C RET C3

see .aaa
47573F 47571D ( field +1E ) CALL E8D9FFFFFF

see .bbb
47575F 47571D ( field +1E ) CALL E8B9FFFFFF

see ttt
47579F 4 # EBP SUB 83ED04
4757A2 EBX 0 [EBP] MOV 895D00
4757A5 47573F ( .aaa ) CALL E895FFFFFF
4757AA 0 [EBX] EAX MOV 8B03
4757AC 0 [EBP] EBX MOV 8B5D00
4757AF EAX 0 [EBP] MOV 894500
4757B2 47575F ( .bbb ) CALL E8A8FFFFFF
4757B7 0 [EBX] EBX MOV 8B1B
4757B9 0 [EBP] EBX ADD 035D00
4757BC 4 # EBP ADD 83C504
4757BF RET C3

There are 11 instructions in TTT, 1 each in .AAA and .BBB, and 7 in
the DOES> part of FIELD (called by both .AAA and .BBB). This results
in 11+1+1+7+7 = 27 instructions executed. By comparison, when my
version of FIELD is used, TTT looks like this:

see ttt
47577F 4 # EBP SUB 83ED04
475782 EBX 0 [EBP] MOV 895D00
475785 0 [EBX] EBX MOV 8B1B
475787 0 [EBP] EAX MOV 8B4500
47578A EBX 0 [EBP] MOV 895D00
47578D EAX EBX MOV 8BD8
47578F 4 # EBX ADD 83C304
475792 0 [EBX] EBX MOV 8B1B
475794 0 [EBP] EBX ADD 035D00
475797 4 # EBP ADD 83C504
47579A RET C3

Now there are only 11 instructions executed. This is less than half of
what the CREATE DOES> version requires. This is for SwiftForth on the
Pentium, but results should be similar with other compilers and
processors. The speed difference is much greater than the 27/11 ratio
implies though. As a rule of thumb, what primarily kills the speed on
microprocessors are jumps, calls and returns. On processors with a
prefetch-queue (such as the 8088), jumps empty out the prefetch-queue.
On modern processors that concurrently execute instructions, jumps
prevent the concurrent execution of instructions. This is also why the
MACRO: word improves the speed --- because it gets rid of one CALL and
one RET instruction. See Michael Abrash's books on 8088 and Pentium
assembly-language for a more in-depth discussion of this effect. The
upshot that the novice needs to remember, is that on most modern
processors the CREATE DOES> version of FIELD will be an order of
magnitude slower than my version.
0 new messages