[HN Gopher] My history with Forth and stack machines (2010)
___________________________________________________________________
My history with Forth and stack machines (2010)
Author : panic
Score : 86 points
Date : 2021-02-20 12:02 UTC (10 hours ago)
(HTM) web link (yosefk.com)
(TXT) w3m dump (yosefk.com)
| gsmecher wrote:
| I return to this essay about once a year, when I hear the siren-
| song of minimalism and am tempted to implement a Forth CPU. It's
| situated exactly in the no-mans'-land between FORTH and "C" camps
| and effortlessly slides between pragmatic and idealistic
| considerations. It emphasizes the distinction between the lure of
| toolbuilding (making a Forth, making a CPU) and actually solving
| the problems these tools are intended to solve. The comments on
| word sizes and problem-shifting are spot-on. It's an excellent
| essay, and I wish there were more like it.
|
| By day, I'm an FPGA coder. (The ASIC situation rhymes, more or
| less.) From the perspective of implementing small CPU
| architectures: register files are not the problem in the way
| you'd expect. A modern FPGA has primitives that implement 32- or
| 64-register files with remarkable efficiency, in 2-, 4-, or
| 8-port varieties. Midrange FPGAs are now fast enough that a
| lightly pipelined RISC CPU performs well enough; there's no need
| to aggressively pipeline a CPU for normal clock rates (100 to 250
| MHz) any more, and little motivation for exotic architectures.
| The MicroBlaze (Xilinx) or NIOS-II (Altera) soft core CPUs are so
| boringly effective they've forced ARM to license their designs
| for FPGAs _for free_. For smaller state-machine-class problems,
| the PicoBlaze is an interesting, minimal RISC. For most of us,
| CPUs are solved problems and the frontier has shifted towards
| cache-coherency, networking and other more interesting arenas.
|
| I love the idea of stack machines in the right contexts
| (PostScript, FORTH, Java, you name it), but it's hard to disagree
| with the essay's conclusions.
| vok wrote:
| Does anyone know of other places where the word "flow" is used
| like this?:
|
| > Good Forth programmers arrange things so that they flow on the
| stack.
|
| I think this concept of "flow" applies elsewhere, for example
| when creating tacit programs in J. But I haven't seen "flow" used
| like this anywhere except this article.
| guerrilla wrote:
| At the same time it reminds me of tacit programming/point-free
| style[1] in general and dataflow[2] programming.
|
| 1. https://en.wikipedia.org/wiki/Tacit_programming
|
| 2. https://en.wikipedia.org/wiki/Dataflow_programming
| peter_d_sherman wrote:
| >"To this day, I find it shocking that you can define defining
| words like CONSTANT, FIELD, CLASS, METHOD - something reserved to
| built-in keywords and syntactic conventions in most languages -
| and you can do it so compactly using such crude facilities so
| trivial to implement. Back when I first saw this, I didn't know
| about DEFMACRO and how it could be used to implement the defining
| words of CLOS such as DEFCLASS and DEFMETHOD (another thing about
| Lisp they don't teach in schools). So Forth was completely mind-
| blowing."
|
| It is, isn't it?
|
| Forth is sort of "the next level up" from assembler, in the
| language abstraction hierarchy...
|
| First you have switches...
|
| Then you have punched cards...
|
| Then you have a simple assembler that converts files of numbers
| into machine code.
|
| Then you have a simple assembler that converts files of
| instruction symbols (operands/mnemonics) and numbers into machine
| code.
|
| From there, you might have assemblers that understand global
| variables and labels/addresses in memory (necessary for function
| calls), etc.
|
| Forth starts to come into existence when you take a later (in
| evolution/time) assembler's symbol (aka "function name") address
| look-up table for functions -- and implement it dynamically (with
| the ability to add to it) at run-time.
|
| That's because the initial Forth tokens/symbols (it's
| "primitives") -- are CALLs to static pre-defined assembler
| routines (or would have been in the first version of forth) --
| with the ability to define new tokens/symbols -- in terms of
| combinations of executions the previous ones...
|
| To make all of this magic happen, you need to separate the "CPU
| provided" single stack (well, at least if we're thinking Intel,
| which may have not been the case with early CPU's) -- into two
| stacks, one for data, one as a call (function return) stack, and
| this is exactly what Forth does.
|
| Forth exposes the data stack to do whatever you want with, to you
| the programmer.
|
| Now, when we get to Lisp (the next level up from Forth, in my
| opinion) -- Lisp does two additional things, which are
|
| A) Takes the ability to monkey with any stack, away from the
| programmer.
|
| B) Implements the management of LISTS -- whose management (memory
| management, pointers, etc.) the programmer no longer needs to
| worry about (think of this as a pattern from mathematics, N, that
| is, 'multiples', 'batches', 'vectors' ("more than one thing of")
| -- meets Forth's ability to work with single symbols -- well
| there has to be a convenient data structure to work with multiple
| symbols (and data, which now there is a demarcation of in LISP)
| at a time -- and that way LISP provides, with it's "I'll manage
| the stack and the lists (at least the memory/structure/pointers
| of the list) for you, so you the programmer don't need to worry
| about any of that!
|
| So yes, Forth is brilliant and fascinating in its simplicity...
|
| I wouldn't write million-line business applications in it,
| because as a programmer, I am not perfect, I make mistakes, and I
| tend to like type-checking, automatic stack management, and other
| things that modern compilers provide.
|
| But Forth is utterly brilliant from a "how did Computer languages
| evolve" / "how could a computer 'pull itself up by its
| bootstraps' perspective"...
|
| It's highly worthwhile for any programmer to learn about...
| macintux wrote:
| The parallels between Forth and Lisp seem painfully obvious to
| me, but I've had knowledgeable programmers here tell me I'm
| hallucinating when I say that. I really need to spend more time
| with both.
| peter_d_sherman wrote:
| Both Forth and Lisp (and Basic, and every other interpreted
| language) -- implement symbol lookup at runtime, that is,
| take an arbitrary text string like 'something', 'something
| else', 'FUNC1', 'bob', etc. -- and look up an address for
| that value, then CALL that address (make that function
| happen).
|
| Modern programmers might understand this as a hash lookup, or
| a key/value lookup... The key is the function/functionality
| name (symbol, string, call it whatever you will), and the
| value is an integer, which is the address in memory that will
| be called to implement the functionality.
|
| Forth doesn't hide anything from the programmer.
|
| Lisp hides its implementation of its list structure (memory,
| dynamic memory allocation, dynamic memory deletion, etc.) as
| well as any and all stacks -- from the programmer.
|
| Now lisp might have API's where that information is
| accessible to the programmer -- (in theory, any language can
| have an API which allows access to any and all memory, by
| definition this allows any language to play with all parts of
| the machine), but the way LISP was originally implemented,
| the programmer no longer has to deal with this, much in the
| same way that in Java/.NET, the programmer doesn't have to
| deal with deallocating memory for objects when they go out of
| scope...
|
| But all interpreted languages, one way or another, boil down
| to 'look up a symbol/string/"key" -- representing a function
| name -- then call/goto/invoke that function in memory... sort
| of like a key/value lookup (or even just a variable name that
| gives you back an address -- a pointer in compiled
| languages), and then you just CALL (preserving where you came
| from (to later RET)) to that address"...
|
| So in some ways similar, in some ways different...
| dasyatidprime wrote:
| Note that (a usual) Forth (here I'm centering on ANS Forth)
| uses early binding for most code, rather than late binding
| as you sound like you're talking about. The dictionary is
| indeed available at runtime, but runtime _includes_
| compile-time!--which is how a lot of the metaprogramming
| works, by interleaving the two with no strict phase
| separation. So it 's while you're _defining_ a word that
| calls other words that the other words are looked up, and
| not while you 're executing it. This has some convenient
| implications for redefinitions: : foo (
| something ) ; : bar 2dup foo 1+ * ; \ or whatever
| \ ... in something loaded later on ... : foo (
| other thing entirely ) ; : xyzzy foo bar ; \ calls
| later foo, then earlier bar which calls earlier foo
|
| ... which means it's harder to run into problems with
| stepping on "internal" symbols even if you're using short
| names in the "same" dictionary. "Purely" interpreted Forth
| does happen at the top level, in which lookups happen just
| before execution, but the part of it that isn't being used
| for compilation tends to be small fragments to kick off
| execution or to enter commands interactively. (In
| colorForth, phase distinctions are made by color instead,
| but I believe it also does early-bound compilation.)
|
| In (a usual) Lisp (here I'm centering on Common Lisp and
| Scheme), function and variable lookups default to late
| binding, but based on symbol objects rather than strings.
| The symbols themselves contain pointers to function objects
| and/or variable contents, so there's a level of
| indirection, but it's not a hash table lookup every time.
| (Indeed in CL you can have "uninterned" symbols which
| usually have a name but can't be looked up by that name.)
| Again the mapping from strings, that is, packages in CL,
| obarrays in Emacs Lisp, etc. generally remains available at
| runtime, so you can do (find-symbol "FOO"), but when
| processing definitions, the lookup from string to _symbol_
| happens at _read_ time. Then in CL there 's some
| restrictions on redefinitions (http://www.lispworks.com/doc
| umentation/HyperSpec/Body/03_bbc...) that make it easier
| for compilers to do more localized early binding even
| between top-level definitions.
|
| Traditional BASICs I vaguely recall being closer to "fully"
| interpreted than this, but I haven't studied those enough
| to say--and I wouldn't expect that to apply to later BASIC
| revivals after a certain point.
|
| An interesting other example would be Lua, which uses late
| binding semantics for global and method lookups, and then
| (here I'm thinking of the mainline PUC implementation)
| makes them faster by interning _all_ strings at the C
| level, in a way which isn 't exposed to the Lua level. When
| all strings with the same contents have the same pointer,
| string equality is very cheap, which makes table lookups
| over the string objects cheap. But lexical locals are
| compiled into index lookups, which are even cheaper, and
| it's often considered good practice to write internal file-
| level definitions as locals, import external module tables
| as locals, etc. so in practice once again some of the late
| binding disappears along the way.
| macintux wrote:
| This really is an interesting article, and much of the
| introduction aligns with my experience: I love the concept, wish
| I had a reason to use Forth, but also I'm not and never have been
| a hardware person and haven't the foggiest clue what to do with
| it.
|
| For anyone who isn't familiar with the language, Thinking
| Forth[1] and Starting Forth[2] are (as far as I know) well-
| regarded books on the subject.
|
| [1]: http://thinking-forth.sourceforge.net
|
| [2]: https://www.forth.com/starting-forth/
|
| Also, for anyone like me who dearly misses W. Richard Stevens, he
| wrote a Forth primer in the 70s. You can find it any several
| other documents on forth.org[3].
|
| [3]: http://forth.org/tutorials.html
|
| Previous discussions of this post over the years:
|
| https://news.ycombinator.com/item?id=3963896
|
| https://news.ycombinator.com/item?id=8146306
|
| https://news.ycombinator.com/item?id=8869150
|
| https://news.ycombinator.com/item?id=21153555
| ranma42 wrote:
| In that context, mecrisp forth on a small micro[1] for doing
| simple embedded/sensor programming sounds like something I
| should try sometime... [1] https://jeelabs.org/article/1705c/
| mumblemumble wrote:
| re:factor is a nice blog for seeing how higher-level things can
| be done in a concatenative language. It's been dormant for a
| couple years, but the archives have some interesting examples
| of redoing well-known stuff in Factor.
|
| https://re-factor.blogspot.com/
|
| For example, here's a gopher server: https://re-
| factor.blogspot.com/2016/10/gopher-server.html
| jlg23 wrote:
| > I love the concept, wish I had a reason to use Forth, but
| also I'm not and never have been a hardware
|
| High level & easy: chat bots. I have implemented a forth
| interpreter in several bots I wrote (admins only, for obvious
| reasons ;). Since they are running within the bot's code, you
| have as much access to the client's state as you
| allow/implement.
| picks_at_nits wrote:
| "When you're holding a Shunting Yard Algorithm, every Domain-
| Specific Expression Language looks like a compile-to-RPN-and-
| evaluate-with-a-stack-machine nail."
|
| https://en.wikipedia.org/wiki/Shunting-yard_algorithm
| [deleted]
| veltas wrote:
| > ...it is scripting that is the best trojan horse for pushing a
| language into an organization. Scripting is usually mission-
| critical without being acknowledged as such, and many scripts are
| small and standalone. Look how many popular "scripting languages"
| there are as opposed to "systems programming languages". Then
| normalize it by the amount of corporate backing a language got on
| its way to popularity. Clearly scripting is the best trojan
| horse.
| jstanley wrote:
| This was a really interesting article. I appreciate the author's
| matter-of-fact style, and the way he communicates his own
| thoughts without trying to say they're the _only_ correct
| thoughts. And despite the author 's own experiences, I came away
| thinking that I would actually like to try writing something in
| Forth myself. Even though I like "C". This description really
| resonated with me:
|
| > being able to do what at least 3 people in their respective
| areas normally do, and concentrating on those 3 things at the
| same time. Doing the cross-layer global optimization.
|
| I at least _desire_ to be competent at that level, and think I 'd
| enjoy having a go.
| jacquesm wrote:
| Forth is an interesting language but if you are used to modern
| stuff it is likely going to be an exercise in frustration to
| get 'something done' unless 'something' happens to be a small
| soft real time project. So I suggest you start with that and
| then take it from there.
___________________________________________________________________
(page generated 2021-02-20 23:01 UTC)