
Show HN: Easy Forth - skilldrick
http://skilldrick.github.io/easyforth/
======
richdougherty
Nice!

If you like Forth, there's information about similar languages over at
[http://concatenative.org/](http://concatenative.org/).

------
vive-la-liberte
I often use dc(1) so my first instinct was to type "1 2 3 +" into the input
without intermediate linebreaks. I was delighted to see that this was
correctly understood to mean what I wanted to express instead of it doing
something silly like taking just the first int of my string or trying to parse
the string as a whole straight to int.

These are the sort of things which encourage readers to read on :)

Edit: Unfortunatelly, when I try to scroll and read the rest, the input field
steals focus rendering me unable to continue. Firefox on Android.

~~~
brudgers
Not directly a solution, but there is GNU Forth for Android.

[https://play.google.com/store/apps/details?id=gnu.gforth&hl=...](https://play.google.com/store/apps/details?id=gnu.gforth&hl=e)

------
danbolt
This is an enjoyable read! I've been coming across mentions of Forth much more
often lately, so it feels satisfying being able to get a sense of it.

One thing that caught my attention is that Forth's boolean value for "false"
is 0, and for "true" is -1. This makes sense if you look at their binary
values, being 00000000 and 11111111, respectively. Does anyone know if there
was an underlying design decision for this? Fast hardware checking? Bit
masking tricks?

~~~
TazeTSchnitzel
If you define it like that, you don't need separate boolean and bitwise AND
and OR. 11111111 & 00000000 = 00000000, 11111111 | 00000000 = 11111111,
~11111111 = 00000000 etc.

QBASIC (and possibly other Microsoft BASIC dialects?) did the same thing.
There's no &&, || or ! in QBASIC.

If you define "true" as just 00000001 then it works for & and |, but not for
~.

------
peter303
Forth programs are very compact. And so are Forth intepreters. Great for 1970s
PCs with memories as small as 8K. But postfix programs are hard to understand.
An hour later and you've forgotten what you have written.

~~~
klibertp
Also great for myriads of devices with constrained memory and computing power
we use every day in 2015.

> But postfix programs are hard to understand

No.

Badly written programs are hard to understand. That's true for every language.
Also, programs written in a language you don't know are hard to understand.
That's obvious, right? Postfix, prefix or infix notations have little to do
with this.

There are natural languages written from right to left, and also ones written
from right to left and vertically at that. Are they "hard to understand" for
people who use them? What do you think?

~~~
TazeTSchnitzel
The direction a language is written in has no bearing on how easy it is to
understand. Postfix is a different matter altogether.

~~~
klibertp
Wait, why? Could you elaborate? How is:

    
    
        + 1 2
    

fundamentally different from:

    
    
        1 2 +
    

? You just look for the operator on the other side of arguments.

I know both prefix, postfix and "crazy infix" (J, some APL) languages and I
really don't see a qualitative difference. Of course, after you overcome the
initial hurdle and get used to the notation.

EDIT: ok, J/K/APL are a special case and I shouldn't mention them.

~~~
TazeTSchnitzel
That's too trivial an example. When you get something more complex, the
difference becomes clearer.

Can you parse the following easily?

c_z x * s_z y * s_y * c_y z * + c_x * c_z y * s_z x * - s_x * - d_z =

Compare that to the infix form:

d_z = c_x * (c_y * z + s_y * (s_z * y + c_z * x)) - s_x * (c_z * y - s_z * x)

With infix, the operator sits between its operands, so you can easily see what
expression makes up the first operand, and which makes up the second: you just
look to its left and its right. Postfix, however, requires you to read the
entire expression, because the operator doesn't show which operands it has,
they're just whatever the last two were, and those last two might themselves
be complex expressions. This gets worse the longer the total length of the
expression.

Postfix also suffers from not making it clear how many operands a given
operator takes. With infix this is always clear.

I like postfix languages for their simplicity, but I refuse to pretend they
are as easy to read as infix languages.

(example was taken from
[https://en.wikipedia.org/wiki/3D_projection#Perspective_proj...](https://en.wikipedia.org/wiki/3D_projection#Perspective_projection))

~~~
klibertp
Well, for me _both_ examples are totally unreadable. I think math's is the
most unreadable notation ever and even a cross between PERL and Brainfuck
would be better. Mathematicians are masochists, and I refuse to follow their
lead. I prefer _meaningful_ variable names, good use of horizontal and
vertical whitespace, context-free grammars, and the like. So maybe this is the
difference and the reason for me perceiving the notations as more or less
equivalent: I'm not biased, as most people, in favor of infix, but rather
biased the other way.

Anyway, let's try doing something with your postfix example (I hope it
displays alright; also, I think you made a mistake in your translation to
postfix, but I left it as it is):

    
    
        c_z   x *
        s_z   y *
            s_y *
        c_y   z *
        +
            c_x *
    
        c_z   y *
        s_z   x *
        -
            s_x *
        -
    
            d_z =
    

Now, this is much _more_ readable than unformatted infix version you give and
that's before factoring this into smaller parts. I didn't read that much of
Forth, but I'm 97% sure that it would be factored into 3 or 4 words, I think.
And it also has an advantage that you read the operation in order they're
going to be executed, while with infix you need to read the entire expression
to know which computation occurs first.

Of course, it's only _more_ readable _for me_ , with my particular background
knowledge and habits; I'm not saying this is or should be the same for anyone
else. _But_ , if there are people who see one form as more readable and people
who see the other form as more readable, then I think that's a solid argument
in favor of both forms not being drastically, qualitatively different.

The problems you point to are definitely real; it's true "the operator doesn't
show which operands it has" by default (for example) and you need to go out of
your way to show it. But infix notation has it's problems too, and you need to
work around them as well. Like operator precedence, which is frankly a
terrible idea.

> Postfix also suffers from not making it clear how many operands a given
> operator takes. With infix, this is always clear.

No, not always. J has words which take one argument on the left and, for
example, two on the right. Or three. Or a variable number of arguments on the
left (granted, they are `tied` with ` word, but still) and nothing on the
right... And Ken Iverson says it's very readable! (To him, at least).

To summarize: I'm still not convinced that there is a major and unfixable
difference in readability between the notations, and I still think you can
make all 3 notations as readable as any other.

------
zatkin
Everything is great, simple, and easy to understand, and then I get to that
Snake example and the code is nearly unreadable.

~~~
abecedarius
The Snake code is more complex than it needs to be -- example: defining
directions as constants from 1 to 4 and then using IF on the direction,
instead of defining directions as offsets in the coordinates. But simpler code
would not be _that_ much simpler -- Forth just takes getting used to, and I
guess the step up to a whole game was too steep. Add some smaller exercises
first? Like Sokoban?

(Generally the main thing that'd make Forth more readable is local variables
in place of stack manipulations.)

~~~
voltagex_
Raise an issue about it?
[https://github.com/skilldrick/easyforth/issues](https://github.com/skilldrick/easyforth/issues)

------
david-given
A couple of months ago I wrote a Forth interpreter, because I'd always wanted
to. I haven't really used it in anger, but passes the basic ANS Forth tests,
so it should be reasonably complete.

(It's here:
[https://github.com/EtchedPixels/FUZIX/blob/master/Applicatio...](https://github.com/EtchedPixels/FUZIX/blob/master/Applications/util/fforth.c)
It's a single, portable C file which is also an executable shell script
containing an awk script! It compiles to about 8kB of code on a
microcontroller.)

From the experience I learnt two main things about Forth:

(a) the realisation of how Forth works, and the way in which the language
bootstraps itself out of nothingness, and the way in which it takes about two
basic principles and then builds an entire language out of them, is truly mind
expanding. The process was full of 'aaah!' moments when it all came together
and I realised just how elegant it was.

(b) actually _engineering_ a Forth interpreter, and dealing with the ANS spec,
was an exercise in frustration. Those elegant principles are compromised at
every stage of the process. The spec defines things which no sane person would
define. I'd implement a word, and it'd be clean and work, and then the ANS
tests would fail and I would realise that the specification dictates a
particular half-arsed implementation which makes no sense whatsoever. The
process was full of 'uuugh!' moments when I saw a thing in the spec and
realised how much more complicated it would make my life.

Examples follow:

\- double words are pushed onto the stack in high word / low word order.
Regardless of whether your architecture is big or little endian. Good luck
with using 64 bit load/store instructions!

\- DO...LOOP is defined to use the return stack for temporary storage. Valid
Forth programs can't call EXIT from inside a DO...LOOP structure. If you try,
your program does a hyperspace jump and crashes.

\- BEGIN...REPEAT is defined _not_ to use the return stack for temporary
storage. Valid Forth programs are allowed to call EXIT from inside a
BEGIN...REPEAT structure.

\- DO...LOOP has different termination characteristics depending on whether
you're counting up or down.

\- Mismatched control flow structures are not just not detected, but they are
actually, in certain combinations, defined to work. The spec actually defines
what some of them do --- IF, THEN, BEGIN, WHILE, REPEAT, if I recall correctly
--- and lets you mix and match them. Good luck if you want to use different,
more efficient implementations.

\- The memory model assumes that Forth is the sole owner of the memory. It
starts at the bottom and works up. When compiling a word, you have to decide
what address it's being written to before you know how long it's going to be.
Want to share the heap with something else? Good luck with that.

\- Division. How overcomplicated can it be? Answer: extremely.

\- Rearranging values on the stack gets old very, very, _very_ quickly.

\- Forth isn't typed! Except where it is, and it doesn't check them, and if
you get them wrong my the gods have mercy on your soul, because the
interpreter surely won't.

I would still say that anybody with any interest in programming should learn
at least the basics of Forth, and should write at least the core of a Forth
interpreter. (It won't take long, and you'll learn a hell of a lot.) But I'd
be really hesitant about recommending it for real programming use, other than
for the special niches where it excels, such as embedded systems. Most of the
problem is that it's overspecified; it would be so much simpler, faster, and
easier to understand if the spec had more undefined behaviour in it. I now
understand why so many people just ignore it and write their own dialect.
Strong type checking would really help, too.

------
tonyonodi
This looks amazing! Thank you. I've been meaning to learn Forth since forever
now.

------
berntb
Fun environment.

A couple of weeks ago I installed 8th to learn a Forth as a hobby. (It
promised iOS integration, so it might even be useful. I'm not there yet. :-) )

