Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Forth: The programming language that writes itself (ratfactor.com)
231 points by ingve on Feb 25, 2023 | hide | past | favorite | 85 comments


My biggest problem with Forth is that you simply cannot start reading a program at an arbitrary point because you only have few valid entry points where you know that the stack is empty. Otherwise you just have no idea what's going on.

This is where it differs from languages which try to guard the scope, but is, in a way similar to the problems created by violating the locality of the problem by using global variables.

I feel like Forth might have evolved to mitigate this problem (as well as many similar ones, like not needing to specify the number or names of arguments to the subroutines, or not really having much structure to the routines that can be expressed through something like paired delimiters or indentation...) but, unfortunately, it didn't. Or, maybe I never found any descendants that would take the language further towards implementing some of the more modern ideas about how to structure large programs.


I've only dabbled in forth so I'm not the right one to answer - but I just want to mention there are other stack based or concatenative languages like Factor and Joy that have had a lot of work put into them. From what I recall they feel like they were created and maintained by somewhat eccentric people... they may have put more thought into the problems you mention


In Factor the stack effects are static and annotated.


They are also generally not as low-level as Forth, and thus don't have the same "easy to port to a completely new arch from scratch" thing going for them.


That's interesting. I'd have to try to find out more about these. Thanks!


Many forths have type annotations for function definitions, summarizing the cumulative stack effects. In some cases these are rigorously enforced by the compiler.


They're not really type annotations (because Forth is still basically untyped), but stack effect annotations. They're just a comment in parentheses that tells human readers of the code how many items are consumed/produced and (very roughly) what type they are:

  : SQUARE  ( n1 -- n2 )  DUP * ;
I've never run into any checkers for these comments -- though https://stackoverflow.com/a/40321950/68707 indicates that checkers may exist. For example, Gforth (GNU Forth), one of the most popular and full-fledged Forth compilers doesn't do this.


StrongForth (http://www.arestlessmind.org/2009/02/03/intro.html) is a statically typed Forth that checks (and requires!) the annotations. It traces how the stack changes after every operation, and guarantees that every function is passed the correct number and types of parameters, and leaves the correct parameters on the stack when it returns. It's impossible to underflow the stack, and overflows are as likely as in C/C++/etc.

To do this, there are some limitations on how the stack can be used compared to regular Forth. (For example, both sides of an IF/ELSE must leave the same number of items on the stack with the same types, but they can have different values. An IF without an ELSE can never change the depth of the stack or what types are on it, since the implicit, empty ELSE side says that the stack doesn't change.) This generally isn't a huge problem, as the things you can't do in StrongForth are usually not such a good idea from a maintenance perspective in regular Forths.


In case it's new to anyone, I recommend going through the jones forth "literate program" - even if you don't understand assembly. You can try porting it to a language you know, it gives a good understanding of how forth works

https://github.com/nornagon/jonesforth/blob/master/jonesfort...


Oh, nice, Jones forth! I second the recommendation to read through it. I ported this to ARM assembly years ago, and it was a lot of fun. No idea where that program is now, unfortunately.

Now that I'm using Apple Silicon day to day, maybe it's time I try rewriting it for ARM64.


It gets hard when you discover that apple has mostly disallowed self modifying code on their arm architecture. You can, with lots of hoops, but a straightforward assembler in forth becomes all but impossible.


Also just learn assembly. The basics take a half hour to grasp if you have any exposure to programming :)


And make it a fun ASM. Like 6502 [0].

[0] https://skilldrick.github.io/easy6502/


My favorite learning assembly. It all mostly applies to every other Von Neumann machine too.


There are at least 2 ports to C that I'm aware of. Here's the older one: http://www.dst-corp.com/james/JonesForthInC-V148.html


Learning languages like these feels like the premise of Arrival(2016) where learning a new language expands the way you think about and solve problems. I'm trilingual like most Indian people but I suspect learning languages like Japanese or Mandarin which use a different, pictorial way of representing words would be a similar way to improve cognition.


LISP is worth learning for a different reason – the profound enlightenment experience you will have when you finally get it. That experience will make you a better programmer for the rest of your days, even if you never actually use LISP itself a lot. (You can get some beginning experience with LISP fairly easily by writing and modifying editing modes for the Emacs text editor, or Script-Fu plugins for the GIMP.)

It’s best, actually, to learn all five of Python, C/C++, Perl, and LISP. Besides being the most important hacking languages, they represent very different approaches to programming, and each will educate you in valuable ways.

http://www.catb.org/~esr/faqs/hacker-howto.html#skills1


> LISP is worth learning for a different reason – the profound enlightenment experience you will have when you finally get it.

I like Lisp (or rather Scheme and Racket), but I have never understood this perspective. The only thing I can think of that Common Lisp does truly uniquely is its condition system. I feel like something like Prolog is much more profound in the way that it reshapes how to think about programming.


What LISP has, uniquely, is its macro system which, unlike text-based macro system like C, operate on the abstract syntax tree level.


For sure. Additionally, the syntax of lisp also makes these AST macros ergonomic and, in many cases, beautiful. Rust has AST-transforming macros, but they're harder to use because the syntax is more complex. They're also quite ugly.


Is that enlightening though? For example, Common Lisp, Scheme, Racket, Clojure, Elixir, and Prolog all have abstract syntax tree macro systems that all differ from one another. And I would argue that macros should, in general, be avoided. They can be very enabling though to extend the given language when used with constraint and moderation.


prolog is another step that's for sure but lisp is the gateway drug

the emphasis on thin syntax, homoiconicity is really no joke to me. I'm finishing queinnec's l.i.s.p but writing some of it in python and using OO requires so many layers and layers to reacreate what is mostly various sexp trees

it puts away a lot of sequential imperative, replaced by functional/function oriented, it may look trivial today but it's been like this since the 70s

and about CL/CLOS, one could mention the MOP, the multimethod+combinations too


This would be my 2023 list: Python, C, LISP, Fortran, Haskell, Prolog, SmallTalk. Also likely Rust, it might replace C in my list and is quite similar to Haskell in some ways, particularly when it comes to turning runtime errors to compile time error.


+ Erlang (or Elixir if you must)


May I suggest adding PROLOG to that list? Or Erlang, for yet another different take on programming.


Though I'm not a Rust fanatic, I will say that spending time programming in Rust has made me a better C++ programmer.


Chinese characters (including kanji in Japanese and hanja in Korean) started off as pictographs but mutated over time, becoming so abstract they're probably better thought of as ideographs.

One way in which Japanese may more profoundly affect your thinking is -- Japanese has completely different registers and even entirely different vocabularies depending on whether you are talking to an inferior, an equal or a superior, or someone from your ingroup or outgroup (and these may change depending on the situation!). Proper use of Japanese language -- proper functioning in Japanese society -- requires a sort of constant situational awareness of your social position with regard to the people you are talking to[0]. Given the similarly hierarchical nature of Indian society, I think it may be easier for an Indian to acclimate to this social awareness than, say, an American or Brit... but spending years in Japan may cause a Westerner to become more socially aware than when they first arrived!

[0] The phenomenon of otaku and hikikomori is explainable in such terms: Japanese people with high-functioning autism and other social deficits would, ceteris paribus, be at a greater disadvantage than Westerners in their own societies, so their tendency to withdraw and to pursue hobbies they can engage in largely solo would be much greater. The word "otaku" came from the tendency of such people to address each other always in the most formal terms to overcautiously avoid causing offense.


Try one of the sign languages. Not sure where you are located, but many countries have their own, including India.


> I'm trilingual like most Indian people but I suspect learning languages like Japanese or Mandarin which use a different, pictorial way of representing words would be a similar way to improve cognition.

The writing system for Chinese or Japanese isn't as different as you are probably thinking. It's certainly not pictorial. I think Chinese and Japanese (but especially Chinese) do think about words differently due to their writing system, but it's a much more subtle effect. Probably the larger mind-bending effect of learning a language like Japanese (in my experience) is the fact that its grammar is so radically different from Indo-European languages (and Chinese).

Far more interesting to speculate about, I think, is if we could create a non-linear written language taking advantage of the two dimensional property of paper/screens to write things which aren't forcibly constrained to the 1D nature of speech.


> Far more interesting to speculate about, I think, is if we could create a non-linear written language taking advantage of the two dimensional property of paper/screens to write things which aren't forcibly constrained to the 1D nature of speech.

I think the problem is that human language is inherently one-dimensional. Spoken language involves a temporal succession of sounds, words, sentences. Even sign languages, while the signs may be 2D, there is still a 1D temporal succession of distinct signs. Real texts involve certain limited escapes from one-dimensionality – footnotes, hypertext (which exists even in printed books, with the table of contents, index, "see page 123"), etc – but, still they are only limited deviations from the essentially 1D nature of human language. SignWriting is interesting as a way of writing sign language by using pictorial symbols for each sign, as opposed to using the written form of a spoken language text – but SignWriting is like the CJK languages, they may be 2D at the micro-level of individual characters, but at the macro-level texts are still fundamentally 1D.

If you could invent a truly 2D language, I doubt the human brain would be capable of processing it. Maybe some AI might become fluent in it, but no human ever could. But even software is predominantly 1D – memory is fundamentally a 1D array of numbers, and in some ways it has been becoming more 1D over time (witness the triumph of flat address spaces over memory segmentation) – which makes me think it might be less than entirely natural even for an AI.


There is a Sapir-Whoorf element here. My brain hurts at the thought of trying to read a real 2-D language. But is this due to a fundamental limitation in the nature of the human brain, or is it more akin to the way my brain hurts every time I learn a new tricky piece of math? if I make and become fluent in a 2-D language, will I expand my horizons and be able to think multi dimensionally as well?


What about hungarian language? It's like if it was created by some programmer or mathematician.


I've heard about how difficult Hungarian grammar is. The closest analog I can think of is Sanskrit which we had to study for a few years in school. They're still discovering more things about the language that were lost for thousands of years: https://www.popsci.com/science/sanskrit-puzzle-2500-years-ol...


I remember also when I was studying philosophy. The amount of non consensual traduction and interpretation from ancient greek is amazing. Ans what about the Επιούσιον case.

I agree with you : hungarian is maybe some contemporary equivalent of the great language from the past such as latin, greek or sanskrit, in regard of scientific thinking. Nevertheless, its a agglutinative language, and the logic behind is different.


I've had people that they like the ontological orientation of Mandarin, not so much the fact that the "words" are pictoral but the way they are combined systematically.


I’ve been learning it for over a year because I want to be able to speak to my wife in her native language. So far I’m loving to learn it for the exact reason you said, the way sentences are pieces together is outright fun to me. I’m also amazed by how much information can be packed in a small combination of characters.


It raises the question of the (eventual) link between the grammar of 'spoken' language and our habilty to think the world. I personnaly think almost every human natural languages have the power to express almost everything with the help of circumlocution.


I've had an unexpected epiphany when I dug deeply into Makefiles. For some reason, its spreadsheet-like logic clicked with me an influenced my programs elsewhere.


you're mixing up language and orthography. For most of human history orthographies were secondary and words were not a symbol or set of symbols

In addition, there's plenty of logographic, syllabic, and alternative English orthographies[0] you can try out if you think the orthography itself is what has a cognitive benefit. Blissymbolics[1] is a well-known historically important one to check out though it's more of an auxlang orthography than English-specific. Some even make them for fun![2]

You might also be interested in shorthands (e.g. Gregg or Pitman) which are phonemic

[0] https://omniglot.com/conscripts/english.htm

[1] https://en.wikipedia.org/wiki/Blissymbols

[2] https://old.reddit.com/r/conlangs/comments/3pfp9o/lets_see_y...


Embedded programming on tiny devices hasn't gone away, but the part that seems rather old-fashioned nowadays about Forth's approach is assuming that the programming environment has to be small and simple to run on the device.

These days we program and interact with devices using a powerful machine like a laptop. Being able to do over-the-air updates can be very useful, and interactive debugging can be useful. But there's no reason you can't assume the UI and compiler are on a much more powerful computer that can run a web browser and whatever toolchain you need.

So that means you can do things like autocompletion and static analysis, and that has effects on what kind of language features you'll want to have, even if the target machine and generated code is very small.


> These days we program and interact with devices using a powerful machine like a laptop.

These days? :)

Using a powerful machine to program a weak machine has been a thing for decades. NES games were often developed on comparatively powerful, 32-bit machines like an HP 9000 connected to an in-circuit emulator which served as the game cartridge for purposes of testing.

But being able to talk directly with a running system, even send it new definitions as you write them, is still profoundly powerful, even if you're doing the writing on your Ryzen-powered beast of a laptop. And part of the point of Forth is, you need far, far less machine than you think you do in order to have a suitably powerful, interactive programming environment.


By ”these days” I meant compared to the 1950’s and 1960’s and 1970’s when Moore worked on what became Forth, according to the article. Yeah, mostly before I was born :)

I tinker with Arduino on microcontrollers and, so far, I’ve always uploaded new firmware over USB when I needed to add something new. After starting to use a WiFi-enabled device in a remote location, I can see the benefit of the libraries that allow you to upload new firmware remotely (“over the air updates”). It would allow me to define new commands pretty easily after adding a command line, which I haven’t needed yet.

To want to define new commands on the fly, though, I think there would need to be a reason why you can’t just send another firmware update with the new commands, such as a lack of network bandwidth?

I’m not saying aren’t situations where a Forth interpreter makes sense, but it seems pretty niche, even for embedded systems?


you're right, there are "good" embedded systems, but there are still all sorts of 8 bit processors (some of them the same old suspects) with small on-chip memory embedded in devices. You think those christmas cards that play tinny music are running multivoice synths DSP through GPUs with 18 bit D/A and spare cycles, and will do autocomplete for you if you can get a terminal connected? uh uh.


No, but the people who programmed them probably did so on a linux machine with GBs of RAM and GHzs of CPU


I wouldn’t expect a terminal session at all in that case.


Some very small computers can talk VT100 over RS232.


there's no problem with small computers talking over rs232, even a single bit wordsize (as on the original HP calculators) could pull it off, but thats moving the goalposts. i was replying to the context of embedded cpu chips that still to this day find it economical to not come with the required memory to hold the code for human interface niceties, because it was suggested that they didn't exist.


You missed my point, actually. I wasn't suggesting a command-line shell that has autocomplete should run on the device. I was saying that you usually don't need any developer tools on the device at all.

All you need is a way to upload firmware. All the developer tools can run on the laptop. You can run VSCode and work with tiny devices.

This means that making developer tools small and simple to run on tiny devices has little point anymore, and languages don't need to be small and simple either.


I love forth and stack based languages in general, writing a forth implementation has become my standard way to learn languages. Lately I have been playing with MinForth[0], it transpiles to C so you can effortless mix C in with the forth and get easy access to C libraries, good fun.

0: https://minforth.sourceforge.io


My biggest problem with forth has always been the curmudgeonly-ness of the community, it's like they feel compelled to do things at exactly 180⁰ to how the rest of the computer industry works, even this webpage throws out every norm about how web pages must be navigated, and feels like something from the 1970s while still looking modern.

I love the language, at least part of it, but the community and that 180⁰ shift always feels like any attempt to utilise forth is swimming upstream in monsoon season.


I personally really enjoyed the site layout choices. I viewed it on a phone and it was like you are given a little chunk of info to think about as you scroll to the next.


This is one of its greatest strengths. The Forth community, large parts of which treat Forth as a secret weapon of sorts, has no particular reason to make concessions on the altar of popularity. This attitude helps to attract out-the-box thinkers and people with a tendency to go against established norms.

The SVFIG meetings have been traditionally excellent in showcasing these somewhat unique in today's mainstream programming culture strengths.


It's important to realize why the computer industry works a certain way and how that compares to different approaches. For instance, there's very real issues with stack machines when you try to generalize them to parallel programs - including extracting fine-grained parallelism from sequential code. The most elegant approach is probably dataflow, but that's still a far cry from the simplicity of the FORTH-inspired, concatenative approach. The register-based CPU's we use are a pragmatic, middle-of-the-road approach.


I agree with your larger point that there are important reasons that the industry works the way it does.

However, your example that stack machines have some difficulty with parallelism, doesn't make much sense. I'd argue that on the contrary, parallelism is one of the areas where stack machines and Forth-y languages shine. arrayForth/colorForth is sort of the assembly language for GreenArrays chips, for example (GreenArrays chips are specifically built for parallelism in embedded systems). I'll also note that many VMs are stack machines, including Lua's and Python's, and these languages are the certain way and how that the computer industry works. Both these languages compile to a bytecode that looks suspiciously like a forth if you write it postfix (Lua moreso than Python, as Python makes use of environments which aren't Forth-y, while Lua only does that for globals).

I think the person you were responding to was pointing out that the Forth community departs from industry norms in cultural ways, not in technical ones. I don't think Forth is really that far outside industry norms, it's just more appropriate to compare it to assemblies and bytecodes than to compare it to high-level languages.


GreenArrays use manycore chips based on a processor array approach. So they're using a FORTH-like stack machine on the individual cores, running serial code and not addressing much memory - but that still leaves open the problem of programming the system as a whole.

> it's just more appropriate to compare it to assemblies and bytecodes than to compare it to high-level languages.

But the FORTH community thinks of it as a high-level language, not just as a machine target. And there are additional difficulties wrt. thinking of FORTH in such terms, such as FORTH being an untyped language and providing only limited ways of structuring programs and data, or enforcing modularity. This means that it will probably be quite hard to develop larger programs using FORTH.


> GreenArrays use manycore chips based on a processor array approach. So they're using a FORTH-like stack machine on the individual cores, running serial code and not addressing much memory - but that still leaves open the problem of programming the system as a whole.

My guy, do you really think that no one at GreenArrays ever thought, "Gosh, it would be nice to have some way to coordinate all these parallel processes..." You really think you're the first one to think that? Geez.

Hint: it's coroutines. Like I said, not that different from the way and how the rest of the industry works, though it's notable that colorForth was ahead of the industry in this respect.

> But the FORTH community thinks of it as a high-level language, not just as a machine target.

Please refrain from telling me what I think. You might say that I don't speak for the entire Forth community, and that's true, but neither do you.

I don't think of it as either a high level language or a machine target.

The homogeneous nature of the instructions all operating on the stack lets you do some higher level stuff, similar to the homogeneous nature of Lisp operating on lists enabling higher-level stuff. The parallels between Forth and Lisp aren't surface-level, either: it's trivial to see the conversion between "(+ 1 2)" in Lisp and "1 2 +" in Forth, but it might be less obvious that these have the same effect on the stack in a simple implementation of both languages. Where they differ is that Lisp leans into the higher level features and focuses on maintaining purity while abstracting away stack operations, while Forth makes the stack explicit and focuses on maintaining direct access to processor instructions, and breaks functional purity with operations like dup. This is why I'd say forth is a lot closer to an assembly language than a high level language, even if it's capable of some high-level constructions.


> This means that it will probably be quite hard to develop larger programs using FORTH.

  "Whenever I hear people boasting of millions of lines of code, I know they have greviously misunderstood their problem." - Chuck Moore
Forth is a concatenative language, it transcends/spans categories like "high" or "low level". Typing is trivial to add to the language, but tends not to be common simply due to most Forths being used for embedded development. Structure is also quite free compared to most other languages.


In the case of fine grained parallelism, where named registers allow parallelism that a rigid stack evaluation doesn't -- one can decode the stack instructions using a counter, the stack pointer, assign register numbers off the counter, and generate register-based micro-instructions that can be executed out of order or in parallel in the usual fashion. In this scheme the stack is stored in the register file, and the execution unit (or register-fetch phase if you prefer) has random access to registers.


This appears very insightful, but I can't grok the differences. Got an explainer handy that I and others might start with?


Please note that these are slides for a talk. Regular web page in the works!


every topic people gather around evolves it's own unique community. The topic is a constraint but it only slightly shapes the culture, the biggest influence is its previous form.

This process is far more interesting than any other topic!


yeah, forth and the array languages feel like they've developed in isolation for decades. but i find that incredibly interesting.


A great introduction is the book Starting Forth [0].

It has the most charming illustrations I've ever seen in a text book.

[0] https://www.forth.com/starting-forth/


Didn't need this rabbit hole, but thanks!


Is there a version of this website that works better for non-mobile browsers?


Have you tried Firefox's reader view? I find it very helpful for annoying site designs, especially after adjusting its style settings.


I'd say it works about the same on desktop and mobile. You might prefer something that isn't so wasteful of whitespace and doesn't make you scroll but that could be accomplished in a way that works the same on desktop and mobile.


Please note that these are slides for a talk. Regular web page in the works!


I think you should mention Factor, too, since you mentioned Joy. Factor is amazing. Lots of great stuff have already been implemented, and the code is pretty concise. I found a lot of goodies in the "extra" vocabulary I never thought I would!

Some random stuff I found interesting:

https://github.com/factor/factor/blob/master/extra/bittorren...

https://github.com/factor/factor/blob/master/extra/hashcash/...

https://github.com/factor/factor/blob/master/extra/bitcoin/c...

Interesting talks: https://concatenative.org/wiki/view/Factor/Talks


I can see what this is reaching for

---

but the presentation is just... tedious.

---

inscrutable

---

insufficient payoff for the patience it requires of the reader

---

I gave up half way down the page.

---

There just isn't enough continuity to these little nibbles of narrative

---

Sometimes things are explained

---

explained with citations even

---

other times they're just thrown out with no explanation whatsoever

---

or a wink and a nod, utterly impenetrable to those not in the know

---

and no sense of what's important to pay attention to, and what can safely be glanced past.

---

Could really use some workshopping.


I think these are slides for a talk, dude.


Exactly, thank you. :-)


Were you distracted by your phone?


Stoical is an older forth with an interesting twist. 'string is a quoted string that is terminated by a space. Colon takes a string parameter, to avoid a lot of "compile mode" nonsense.

To define the cube function

  'cube : dup dup * * ;


this is a great idea, among other things because it puts the name you're defining in the left margin where it's easiest to scan to, but i don't see how it avoids any compile-mode nonsense. in this line of code, 'cube still has to push the string on the stack at compile time, and : has to consume it at compile time, while dup just has to compile some code into the colon definition, which at least strongly suggests that the interpreter is being run in a different mode that compiles things like dup instead of executing them?

https://stoical.sourceforge.net/summary.php

https://sourceforge.net/projects/stoical/files/stoical/0.1.8...

some example code

    % %(var) 1+!
    % Increment the variable addressed by TOS.
    % %
    '1+! : dup @ 1+ swap ! ;

    % %(math) hex
    % Set radix to hexadecimal.
    % %
    'hex : 16 radix ! ;

    % %(io) cr
    % Output a newline.
    % %
    'cr : '\n = ;

    % %(io) space
    % Output a space.
    % %
    'space : 32 put ;

    % ----- define the more traditional interface to flow control.
    % %(conditional) if (*)
    % "n IF ... then"
    % Conditionaly execute the instructions. The instructions following IF will be
    % executed if TOS is not FALSE upon invokation.
    % %
    'if     : () if{        exec ; immediate
it seems to be dynamically-typed and garbage-collected?


> 'string is a quoted string that is terminated by a space.

Sounds like LISP symbols, which have the same syntax, are also essentially strings, and are also used to refer to names of function and variables.


I learned about stack based programming when I bought a HP28s calculator in 1988. It could be programmed in its Forth like language, had data structures like lists, arrays, matrices, and symbols. It was also my intro to Scheme. It was like revelation. When everyone else around me was learning C or Pascal, where arrays had fixed lengths, I had seen the future with dynamic data structures. I could implement and test ideas that would take me ten or a hundred times more LOC.

Now over 30 years later I still use a stack to implement tree traversal instead of using recursion.


> Now over 30 years later I still use a stack to implement tree traversal instead of using recursion.

Recursion is the same as "using a stack". It's just using the system stack generally, which might not be what you want.


But it is possible to get a stack overflow error when using recursion to implement tree traversal if the depth of the tree is too large or if the recursion is not properly managed.

Implementing tree traversal iteratively can help avoid that because it does not rely on recursive function calls that can build up on the call stack. Instead, an iterative approach uses an explicit data structure (such as a stack or queue).


Well, you could run out of memory implementing a stack as an array too. It all depends on what the limits are.


I kind of like that because if your tree depth is that large you're throwing away the benefits of a tree generally and that is an indication you should be doing something different.


loved this


[flagged]


Please note that these are slides for a talk. Regular web page in the works!


I didn't even know that edge was on ios


[flagged]


you might wanna look up what all browsers on ios are forced to use




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: