šŸ‘©ā€šŸ’» chrismanbrown.gitlab.io

Counting Apples

The flexibility of implicit functional calls in forth

2021-07-30

There is an example from Thinking Forth that resonated with me that I would like to document here.

It showcases how implicit calls and implicit data passing make Forth a uniquely flexible and expressive language.

About Retro

This markdown file is a valid retro program written in the literate style.

Here are some ways to interact with it:

  1. read it with your eyes

  2. you can run it with retro apples.md. This will execute everything between ~~~ triple tilde fences. (It won’t do anything.)

  3. run the tests with retro -t apples.md. This will execute everything between triple backtick fences.

  4. Open up the repl by typing retro and then type 'apples.md include. Now play around.

Learn more:

Counting apples

Look, we know we have some apples. But how many! We obviously need to write a little forth program to keep track.

Here’s a variable:

'apples var

The word apples returns an address.

You can write to it and read from it:

#20 apples store
apples fetch
'There_are_%n_apples s:format s:put nl

That’s it, life is easy counting apples.

Now let’s write a word to increment your apples count by a bunch:

:dozens (-n) #12 apples v:inc-by ;

Now you can count them apples so fast! Apples by the dozens!

dozens
apples fetch
'Twelve_more_is_%n_apples s:format s:put nl

Kinds of apples

Uh oh, a requirements change! It is no longer sufficient to merely keep track of apples.

Now we must keep a tally of both green apples and red apples!

That’s okay. Remember, apples just returns an address.

First, we need some new variables:

'reds var
'greens var

reds and greens will be our new tally counts. One for each color.

Now, a bit of indirection:

'color var

A new variable. color will be a pointer to whichever color (reds or greens) we are currently tallying. This word will operate mostly behind the scenes. It’s kind of glue.

Look, here come some verb words:

:red @reds !color ;
:green @greens !color ;

red stores the reds total at the color address. green does the same for greens.

Now, to fix our apples var with a final bit of glue:

:apples color ;

apples is no longer a variable, but instead a word (function) that now returns the address of color. Which in turn holds the value of reds or greens, depending. apples still just returns an address. Nothing has broken in our code. You can continue to use it the same way.

Our code from above?

#20 apples store
apples fetch
nl 'Guess_what,_still_%n_apples s:format s:put nl nl

Still works.

Now you can store tallies in greens and reds:

#10 !reds
#20 !greens

and fetch the individual totals:

red apples fetch dup
'%n_red_apples s:format s:put nl

green apples fetch dup
'and_%n_green_apples s:format s:put nl

+ #30 eq?
  [ 'are_30_apples s:put ]
  [ 'That_dont_add_up s:put ]
  choose
nl nl

and our previous apples dozener?

:dozens (-n) #12 apples v:inc-by ;

Yeah that still works too:

apples fetch
'%n_red_apples s:format s:put nl
dozens apples fetch
'plus_12_more_equals_%n s:format s:put nl

Conclusion

Forth’s implicit function calls allow you to change words from variables to functions and vice versa, possibly without having to change any existing code.

Because Forth also has implicit data passing: you don’t have to pass data to functions or actively handle return values. Everything goes on the stack.

And I think that’s neat.