Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Thoughts on Forth Programming (call-with-current-continuation.org)
151 points by andyjpb on Dec 18, 2019 | hide | past | favorite | 90 comments


Can you please give some examples on those who are extremely productive using FORTH? I'm intrigued. I think FORTH could be very useful to someone who prefers to work alone or in small group, but I'm not sure how do they use it in daily life.


Chuck Moore wrote a VLSI tool in 500 lines of colorForth that he's used for multiple chips that he's put on production lines:

(Site mirror as the original site is gone.) https://colorforth.github.io/vlsi.html

John Earnest has a lot of cool things (his work is incredibly underrated), but an example of something he wrote in Forth is Mako, an entire console:

https://github.com/JohnEarnest/Mako

During the days of the U.S.S.R., the Russians independently discovered Forth in DSSP, an incredibly similar language. Multiple universities were filled with DSSP, and it worked incredibly well for them.

Most of the people I know personally who are stopped posting code publicly when Gitorious died, but they're definitely still around. It's a bit like APL, my language of choice: the communities are there, they're just...in hiding.


Do you know of any good code repositories for DSSP?


DSSP is effectively a dead language. There are a few interpreters, but for a long time there weren't, so there hasn't been much code for decades now. It died with the U.S.S.R., like many innovations in computing did.


I wondered too (first I've heard of this) but all I find is in Cyrillic: https://github.com/trinarium/DSSP-C

I did find one short paper in English: http://www.complang.tuwien.ac.at/anton/euroforth/ef00/lyakin...


Here's an interpreter with docs in English:

https://github.com/beadleha/libreDSSP


Mitch Bradley. He wrote the firmware for Sun machines, made an IEEE standard of it, open sourced it for OLPC. Highly productive guy.

Interview with him seems to have disappeared from the web? http://mitchbradley.blogspot.com/2010/08/open-firmware-and-o...


Thanks buddy, this (including the long replies in the same thread) is really interesting. Although myself is not a programmer, it looks very interesting, at least conceptually.


Here's the interview with Mitch Bradley saved on archive.org:

https://web.archive.org/web/20120118132847/http://howsoftwar...

I've previously posted some stuff about Mitch Bradley -- I have used various versions of his ForthMacs / CForth / OpenFirmware systems, and I was his summer intern at Sun in '87!

Mitch is an EXTREMELY productive FORTH programmer! He explains that FORTH is a "Glass Box": you just have to memorize its relatively simple set of standard words, and then you can have a complete understanding and full visibility into exactly how every part of the system works: there is no mysterious "magic", you can grok and extend every part of the system all the way down to the metal. It's especially nice when you have a good decompiler / dissassembler ("SEE") like ForthMacs, CForth, and OpenFirmware do.

https://news.ycombinator.com/item?id=9271644

>DonHopkins on Mar 26, 2015 | parent | favorite | on: The Elusive Universal Web Bytecode

>The other "universal bytecode" that came out of Sun and actually has seen a lot of use is Mitch Bradley's Open Firmware Forth based boot ROMs, used by Suns' 68k, SPARC and x86 boxes, Apple's PPC Macs, IBM, ARM, OLCP XO-1, and many other systems.

>http://en.wikipedia.org/wiki/Open_Firmware

https://news.ycombinator.com/item?id=12240478

DonHopkins on Aug 7, 2016 | parent | favorite | on: “EFI? Intel has been trying to shove that down ou...

>Mitch Bradley originally developed a Forth system at Sun for use diagnosing and developing hardware, by burning it into ROM and running it via a serial port.

>It was based on Langston and Perry's Forth-83, and had a meta compiler that could target different word sizes and architectures. He made it even more architecture and word size independent, implemented interactive top level loops and conditionals, emacs-like line editing, all kinds of low level device drivers and testers that ran in stand-alone mode, and many other features, including full 16 and 32 bit support with a vocabulary for writing word size and endian independent code.

>He ported Sun Forth to 68K and SPARC Sun workstations, as well as the Amiga and other systems. It ran in both stand-alone mode (from disk, tftp or ROM), or under Unix. Under Unix, it could dynamically relocate and link in Unix libraries, and you could call back and forth between Forth and C.

>Sun Forth eventually evolved and standardized into the Open Firmware [1], whose purpose was to support machine independent byte code [2], so plug-in hardware cards could include ROMs with Forth byte code drivers that ran on 68K, SPARC, x86 and other systems.

>Sun shipped it with the SPARC workstations, Apple adopted it and shipped it on their PowerPC Macs, IBM shipped with their POWER servers, and Mitch worked directly with the OLPC project extending OpenFirmware to support the OLPC XO-1 Children's Computer secure and power efficient hardware. [3]

>>OLPC Wiki: Open Firmware

>>Open Firmware is the hardware-independent firmware (computer software which loads the operating system) that the XO runs.

>>It was developed by Mitch Bradley at Sun Microsystems, and used in post-NuBus PowerPC-based Apple Macintosh computers (though it has been dropped with Apple's transition to Intel processors), Sun Microsystems SPARC based workstations and servers, IBM POWER systems, and PegasosPPC systems, among others. On those computers, Open Firmware fulfills the same tasks as BIOS does on PC computers.

>>For example Fedora and Debian use the YaBoot BootLoader for Open Firmware.

>>The Open Firmware user interface includes a FORTH-based shell interface. FORTH is a powerful high level language that is remarkably compact. A complete Forth development environment including compiler, decompiler, assembler, disassembler, source level debugger, and assembly language debugger is present in the XO boot ROM (SPI FLASH). With the Open Firmware Forth system, you can directly access all of the hardware devices on the XO, use built-in functions like selftest diagnostics and games, and even write complete applications, without needing any external tools. The bulk of Open Firmware is written in Forth, so the source level debugger can be used to debug Open Firmware itself.

>[1] http://www.openfirmware.info

>[2] http://www.openfirmware.info/FCODE_suite

>[3] http://wiki.laptop.org/go/Open_Firmware

https://news.ycombinator.com/item?id=15040859

>DonHopkins on Aug 17, 2017 [-]

>Emacs qualifies as a window manager in my book! ;)

>That code you linked to is from Mitch Bradley's "Forthmacs", which ran on Sun workstations including 68k i86 and SPARC, and also Atari ST, Mac and other systems. He developed it into the "Open Boot ROM" architecture, which was used in Sun workstations and Apple PowerPC Macs as well as the OLPC children's laptop.

>https://github.com/ForthHub/ForthFreak/blob/master/Forthmacs

>https://en.wikipedia.org/wiki/Open_Firmware

>http://wiki.laptop.org/go/Open_Firmware

>On SunOS, Forthmacs had a library clink.f with the ability to dynamically relocate and link Unix libraries so that you could call them from Forthmacs, pass arguments on the stack, etc. SunOS didn't actually support shared libraries or dynamic address relocating at that time, so Forthmacs simply ran the Unix linker utility to create a file with the library relocated to the desired address space in the FORTH dictionary, and then read that file into memory, define its symbols in the FORTH dictionary, and let you access its variables and call its functions directly from FORTH!

>That's how Mitch originally integrated MicroEmacs with Forth to make Forthmacs, and how I later integrated "uwm" into FORTH: I refactored uwm so instead of having an event loop in the main function, it was a library that could be called by FORTH, which would link the library in and run the main loop itself, calling into the library as needed to initialize and handle specific events (_uwm_init, _uwm_poop).

>http://www.donhopkins.com/home/archive/piemenu/uwm1/fuwm-mai...

>Here's the glue that links in the uwm library from fuwm.out:

>http://www.donhopkins.com/home/archive/piemenu/uwm1/load-fuw...

    .( Loading...) cr
    requires tasking.f
    requires uwm.f
    requires clink.f
    .( Linking...) cr
    "" fuwm.out clink
    .( Linked!) cr
https://news.ycombinator.com/item?id=17480298

>DonHopkins on July 7, 2018 | parent | favorite | on: Tali Forth 2 for the 6502

>Cool! Does it include a FORTH 6502 assembler written in FORTH? I love writing assembly code in RPN with Forth macros!

>You can write FORTH code with loops and conditionals and any kind of logic and parameters, that dynamically assembles machine code! Much better than your typical macro assembler.

>Here's some 6502 assembler for an Apple ][ SUPDUP terminal emulator that does ram card bank switching:

>http://www.donhopkins.com/home/archive/forth/supdup.f

>Here's some Forth 68k assembler that draws lines on a weird Sun-2 CGONE graphics board.

>http://www.donhopkins.com/home/archive/forth/cg/bline.f

>Mitch Bradley's 68k forth lets you use interactive conditionals and loops in the top level outer interpreter!

>I think he wrote a paper about that feature, which I might be able to dig up ... Here's something related he wrote about refactoring the outer interpreter, but not what I'm thinking of:

>https://groups.google.com/forum/#!topic/comp.lang.forth/lKQj...

>avhon1 on July 7, 2018 [-]

>> Does it include a FORTH 6502 assembler written in FORTH?

>https://github.com/scotws/TaliForth2/blob/master/docs/manual...

>>Currently, there is no assembler included. The plan is to include a simple assembler based on Simpler Assembler Notation (SAN).

>It does include a disassembler: https://github.com/scotws/TaliForth2/blob/master/docs/ch_dis....

>The author has written a 6502 assembler in gforth: https://github.com/scotws/tasm65c02

>Here is a document describing the notation: https://docs.google.com/document/d/16Sv3Y-3rHPXyxT1J3zLBVq4r...

>DonHopkins on July 7, 2018 [-]

>I got a quick reply from Mitch Bradley!

>Yes I wrote a paper but I probably can't find it. The easiest place to look would be in the Open Firmware source.

>https://github.com/MitchBradley/openfirmware/blob/master/for...

>The magic is all in +level and -level. Search for those in kernel.fth to see other places they are used to achieve a similar effect, e.g. in abort"

>For an alternative but effectively equivalent formulation, see:

>https://github.com/MitchBradley/cforth/blob/master/src/cfort...

>and

>https://github.com/MitchBradley/cforth/blob/master/src/cfort...


I love your posts about this stuff :)

I was also an "intern" of sorts for Mitch as recently as 2009 or so. I wrote the audio driver for OLPC XO 1.5 and went to Taipei to help with the bringup. I was amazed to watch Mitch using Forth to interactively tweak the DDR memory settings, find bugs in the motherboard, and find bugs in the CPU which was a godawful x86 from VIA that had seemingly only been debugged enough to boot windows :)


That must have been a fun and unique opportunity! His programming and debugging process has to be seen to be believed. OpenFirmware is definitely a highly polished work of love. Its well-commented and meticulously organized source code reads like fine literature or classical music! Stuff like the metacompiler and kernel, for example:

https://github.com/MitchBradley/openfirmware/blob/master/for...

https://github.com/MitchBradley/openfirmware/blob/master/for...


This subthread was originally in reply to https://news.ycombinator.com/item?id=21821841, but we detached it because it is full of information and can stand on its own.


Forth style is great for writing but very hard for reading. I'm playing around with the idea that you use a Forth-like language to interactively develop code. The steps you perform become AST nodes. Then when you want to read it, it comes out in another form.

This is more ideal because it is more interactive and you can see the immediate result every step of the way. This is different than writing traditional code because sometimes variables and functions are not returning what you expect and you are working blind under the wrong assumption. You then need to debug it to find out where the value is not what you expected and why. Forth wins for writing in my book because you have immediate feedback which lifts some of the cognitive load and saves time on debugging.

However, the problem with reading Forth is that it forces the cognitive load of having to know what is on that stack at every moment. If you make one little mundane mistake then it completely changes the meaning of the code and you get lost. So for reading I think you need to read it in the more traditional language forms.

Some kind of IDE could let you interactively write. But once you save it, you can read it in a more traditional prefix / infix syntax.


Plenty of valuable things that fall out of the norm can be "hard to read" if one doesn't invest any time whatsoever (e.g. Lisp).

The real question is what happens if one does invest the time. In the case of Forth, one needs to commit to memory the standard words. After that point, it reads smoothly and unambiguously (assuming good coding practices and stack effect declarations, bad code that's hard to read can be written in any language).

Same thing goes for Lisp and parentheses.

Alas it is far too easy these days to dismiss things that do not immediately "click" since fewer and fewer people seem willing to make the effort due to conditioning and network effects. This has led to a proliferation of bad languages and a lowest common denominator approach to programming.


> Forth style is great for writing but very hard for reading.

It's the lack of type- and structural indications. I like De Bruijn notation for lambda calculus, which gives you a "postfix" syntax like in Forth (only for function application though, not for lambda abstraction!) but is semantically just plain old lambda, with all that implies (easy to extend with types, no weirdness in stack interactions, etc. etc.). Unfortunately I've not seen it implemented in real-world systems, except perhaps in some theorem provers.


What I would like to have (and looks quite similar to what you are describing) is some APL interpreter (or J or K...) with an RPN input method. The idea comes from HP calculators, where you use RPN to "build" algebraic expressions.

I have many ideas for such a system, but it will require much experimentation to get it right and, at this moment, is just vaporware.


Have you seen Lang5? It's a stack-based array language.

http://lang5.sourceforge.net/tiki-index.php


This quote stood out as sounding quite far-fetched: "on many CPUs the interpreter consists of two or three machine instructions". Can someone point to an example?


In FORTH there is a distinction between the compiler and the interpreter. Each function in FORTH is called a "word". You define a word by giving it a name (historically only the first X characters counted -- often 8). The compiled words are stored in a "dictionary" which is essentially a key value pair with the name of a compiled word and a pointer to its compiled code. When you are compiling a word, you add a new entry to the dictionary. Then for each word contained in the new word you are defining, you look it up in the dictionary and store the pointer to its compiled code. So essentially, the compiled code consists of a list of pointers -- one for each function call you are making.

It's a little more complicated than that, but not much. Numerical literals need to be added to the stack rather than being treated as a pointer to a function, but there are a variety of ways you can tag the information you are putting in the list.

The interpreter works by taking a pointer to the function, and running it. This will essentially jump you to another pointer to a function which you will run. That will jump you to another pointer to a function which you will run. Eventually you will end up pointing to a function that was hand implemented in assembly language (part of the kernel for the language).

This technique is known as a "threaded interpreted language" (TIL). I haven't really explained it very well, but essentially it's just either pushing a literal onto the stack or jumping to a subroutine. The actual code for the interpreter is insanely small because it does virtually nothing (either jump to a subroutine or push a value onto the stack). While the entire thing is not 2 or 3 machine instructions, the actual running functionality would just be looping through something of that size.

One of the nice things about this setup is that it's pretty easy to write an entire FORTH kernel in 16 or 32K. So all of the actually executing code will fit in the cache of even a small processor. The rest of the code is literally lists of addresses and integer literals. They are super easy to fetch and you can also be tricky about optimising how you fetch them. The end result is that you barely ever hit main memory when talking about the code part of the system. And since you prefer working on the stack to working on the heap, you get really good locality on the working memory as well. This can give you insanely good performance with very little cognitive overhead as a programmer.


It the kernel and interpreter typically single threaded? I'm curious how it handles UARTs, IRQs and atomics.


Here's a good doc on multitasking in FORTH: https://www.bradrodriguez.com/papers/mtasking.html


The previous reply explained things pretty much - many implementations have "Words" (i.e. functions) be invoked by just jumping from one to the next.

If you have some spare time reading this implementation is very enlightening:

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

Though I appreciate it is a little low-level it explains things very well, and is readable if you have only a hazy grasp of assembly language.

The specific documentation I was thinking about is this section on calling sequential functions:

https://github.com/nornagon/jonesforth/blob/4f853252f715132e...


The book “Threaded interpretive languages” by R. G. Loeliger has details on how Forth & Forth like languages are built re: core interpreter & threading of words.


That is the best book anyone has ever loaned me and some day I plan on returning it.


See "Moving Forth": https://www.bradrodriguez.com/papers/moving1.htm

The "interpreter" is just the macro that handles execution of the next subroutine in a threaded program. As in: https://en.wikipedia.org/wiki/Threaded_code

It might only be a call instruction, or a pointer increment and a jump. If you have to move the pointer to the working register to increment it, that's three instructions.


FORTH has both an "inner interpreter" and an "outer interpreter" (aka the compiler, but that also works interactively like an interpreter).

The inner interpreter is the thing that threads between words, and is typically an assembly function named "NEXT", which can be just a few instructions. Here is the FIG-FORTH 6502 implementation of "NEXT" (which is a bunch of instructions since the 6502 has 8 bit registers and simple addressing modes):

https://ksquiggle.neocities.org/ff6502.htm

    0122  0244            ;
    0123  0244            ;     NEXT is the address interpreter that moves from
    0124  0244            ;     machine level word to word.
    0125  0244            ;
    0126  0244  A0 01     NEXT   LDY #1
    0127  0246  B1 AE            LDA (IP),Y    Fetch code field address pointed
    0128  0248  85 B2            STA W+1       to by IP
    0129  024A  88               DEY
    0130  024B  B1 AE            LDA (IP),Y
    0131  024D  85 B1            STA W
    0132  024F  20 6F 02         JSR TRACE   Remove this when all is well
    0133  0252  18               CLC           Increment IP by two
    0134  0253  A5 AE            LDA IP
    0135  0255  69 02            ADC #2
    0136  0257  85 AE            STA IP
    0137  0259  90 02            BCC L54
    0138  025B  E6 AF            INC IP+1
    0139  025D  4C B0 00  L54    JMP W-1     Jump to an indirect jump (W)
(This is actually self-modifying code that write the indirect address to jump to into W, the operand of an indirect JMP instruction at W-1, then does an absolute JMP to W-1 to jump indirect through W.)

Some CPUs can implement "NEXT" in one or only a few instructions, and FORTH implementations can use "indirect threading" (like the above 6502 implementation that indirectly jumps through each word's CFA (Code Field Address)) or "direct threading" where the word pointers refer directly to code, or they can even compile words directly to machine language instructions instead of threaded pointers (so there's effectively no inner interpreter, just direct machine language calls), and in that case they can even inline "NEXT" at the end of every word definition for speed, instead of jumping to a global "NEXT" implementation (subroutine threaded machine code).

https://en.wikipedia.org/wiki/Threaded_code#Threading_models

And there are other possible variations and hybrid combinations, like "subroutine threading" or "token threading", which you might want to use to implement FORTH systems in C or other higher level languages, using function pointers (like CForth) or switch statement tokens for built-in primitives (like machine-independent OpenFirmware byte code):

https://github.com/MitchBradley/cforth

https://www.complang.tuwien.ac.at/forth/gforth/Docs-html/Dir...

The outer interpreter is the parser and compiler, which is written in FORTH, and includes a bunch of words, but is extremely simple (and extremely extensible) compared to other language interpreters. The outer interpreter can be in interpret or compile mode (controlled by a variable called "STATE"): when you're typing expressions interactively in interpret state, it executes them immediately, but when you start a word definition (with ":") it goes into compile state and compiles the words instead of executing them.

Except that in compile mode it does execute specially marked "immediate" words, with which you can implement control flow and macros. There are immediate words [ and ] that switch between compile/interpret states, so in the middle of a word definition you can pop out into interpret mode and execute arbitrary computations (like macros or meta programming), then pop back into compile mode. For example you could compute a number, and then compile it as an inline literal!

And then there's <builds and does>, which are word defining words, that let you do certain kinds of meta-programming, implement your own domain specific languages, data types, object systems, and extend the FORTH interpreter and compiler in FORTH.

http://www.forth.org/svfig/Len/definwds.htm

>It has been said that one does not write a program in Forth. Rather, one extends Forth to make a new language specifically designed for the application at hand. An important part of this process is the defining word, by which it is possible to combine a data structure with an action to create multiple instances that differ only in detail. One thinks of a cookie-cutter; all the cookies are the same shape but have different-colored icing.


Here's a cool dynamic WebAssembly based Forth:

https://el-tramo.be/blog/waforth/

>The Interpreter

>The interpreter runs a loop that processes commands, and switches to and from compiler mode.

>Contrary to some other Forth systems, WAForth doesn’t use direct threading for executing code, where generated code is interleaved with data, and the program jumps between these pieces of code. WebAssembly doesn’t allow unstructured jumps, let alone dynamic jumps. Instead, WAForth uses subroutine threading, where each word is implemented as a single WebAssembly function, and the system uses calls and indirect calls (see below) to execute words.

>The Compiler

>While in compile mode for a word, the compiler generates WebAssembly instructions in binary format (as there is no assembler infrastructure in the browser). Because WebAssembly doesn’t support JIT compilation yet, a finished word is bundled into a separate binary WebAssembly module, and sent to the loader, which dynamically loads it and registers it in a shared function table at the next offset, which in turn is recorded in the word dictionary.

>Because words reside in different modules, all calls to and from the words need to happen as indirect call_indirect calls through the shared function table. This of course introduces some overhead.

>As WebAssembly doesn’t support unstructured jumps, control flow words (IF/ELSE/THEN, LOOP, REPEAT, …) can’t be implemented in terms of more basic words, unlike in jonesforth. However, since Forth only requires structured jumps, the compiler can easily be implemented using the loop and branch instructions available in WebAssembly.


Someone has done a port of PostScript (GhostScript) to WebAssembly:

https://chrome.google.com/webstore/detail/postscript-viewer/...

I wonder if NeWS, or something similar, could be resurrected in-browser?


One can get a 6502 to do NEXT in a single instruction and have an IP register in hardware ... if one sticks a coprocessor onto it. Witness the KimKlone.

* http://laughtonelectronics.com/Arcana/KimKlone/Kimklone_shor... (https://news.ycombinator.com/item?id=16564257)



The main problem with such a stack based language for 'serious' programming would, I think, be the lack of dynamic allocations. The stack is all nice and fine if pieces of memory only need to be preserved inside functions and their children but if memory is to be allocated for an amount of time that is determined at run time there basically does not seem to be anything to do that. Any kind of solution to this would probably be pretty convoluted.


I wrote malloc.fth for Mitch Bradley's ForthMacs, which ended up in OpenFirmware:

https://github.com/openbios/openfirmware/blob/d5cc657ce81c0f...

    \ Forth dynamic storage managment.
    \
    \ By Don Hopkins, University of Maryland
    \ Modified by Mitch Bradley, Bradley Forthware
    \ Public Domain
    \
    \ First fit storage allocation of blocks of varying size.
    \ Blocks are prefixed with a usage flag and a length count.
    \ Free blocks are collapsed downwards during free-memory and while
    \ searching during allocate-memory.  Based on the algorithm described
    \ in Knuth's _An_Introduction_To_Data_Structures_With_Applications_,
    \ sections 5-6.2 and 5-6.3, pp. 501-511.
Forth systems running on C libraries can also call back to the native malloc/free implementation, but it's nice to have a pure FORTH implementation for embedded applications.


Fair enough, sounds like a workable solution.


GForth has a word ALLOCATE exactly for that purpose. Allocates a block on heap, returns address. Works on Windows, Linux, Android, embedded


I'm not sure I follow. The Forth stack is not like the C stack.


This can be trivially achieved by moving the dictionary pointer and reserving some space on the heap.


Anyone who wants to see a modern forth with elements of common lisp that is focused on application development should check out Factor.

Concatenative languages are really fun to use once you get over the initial hump.

https://github.com/factor/factor


Factor is a good example of what the writer of the linked article considers:

>...what just ends up being some sort of Lisp with a funny notation.


Where do you think Kitten would end up?

http://kittenlang.org/


I have been looking at Forth programming and one thing I would like to figure out is how to use gforth under Linux in a pipeline of shell and awk. I haven't yet found a practical use for Forth in this environment yet...


Maybe Retro forth is worth looking into in this regard (I haven't really used it myself, but the conception and minimalism around it are really appealing):

http://forthworks.com/retro

http://forthworks.com/retro/book.html#using-in-a-pipe


Forth isn't super practical in such an environment; gForth in particular is a bit "large". (Not that this is bad, but it's of the second school the article mentions.) If you wanted, you could cobble together a Forth implementation that played nicely with pipes. I did this, it works fine, but it's not the type of environment that matches Forth very well.

Forth's design is to give you the machine and let you compute with it rather than to act as part of a pipeline.


> One such property is the use of reverse polish notation and the lack or eschewal of local variables.

What do you gain by eschewing local variables in favor of reverse polish notation that throws values onto a stack?


There are two hard problems in computer science: cache invalidation, naming things, and off-by-one errors. Eschewing local variables means you have fewer things to name.


I never understood the "naming things" part to be about temporary variables. IMO it's about exported APIs, global identifiers, and especially about finding resources by some useful name in a distributed system.

In any case, Forth is a language for implementing your own DSLs by defining a whole bunch of "words" that all need to be named. So it's not like you get around the harder part of "naming things".


I heard the two problems are that we have only one joke and it's not funny.


The code becomes trivial to factor, because you don't have any named variable references that would need to be changed. If you find a sequence of words you want to put in a function, you can literally just cut and paste them out. Likewise, you can inline a word just by copypasting its definition in place.


As someone else mentioned you get many natural factorization points because often you can just copy a portion of code verbatim into a new word.

Of course, you can implement named parameters and local variables in Forth. There is even a standard for locals in ANS Forth.


> In Forth there is only memory - word and byte-sized cells of storage in memory, and stacks. You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout.

Other than in niche or pet projects, can this even work?

If you deal with any sort of multibyte data like Unicode, doesn’t this become way harder?

Or do you just punt and use ascii code pages?


You define words which are like functions which you could use to operate on multibyte data. Most forth code uses large dictionaries of words. So it will look like 'dosomething decode bytes' where bytes is your utf8 and decode is some utf8 decoder and so on.

edit: see https://rosettacode.org/wiki/UTF-8_encode_and_decode#Forth for a forth word for decoding utf-8


Looking at this Forth code as a programmer who has never written any Forth code, I can understand how "ordinary" code looks to non-programmers. That whole RPN business means I cannot even figure out where to start reading.


Well, being confused by RPN is a pretty low bar. Kids used to use it in their math class, on scientific calculators...


Take a look at the book Starting Forth:

https://www.forth.com/starting-forth/


>Other than in niche or pet projects, can this even work?

All kinds of projects have been written in Forth, so yes.

Also, regarding "You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout.", until around the 80s, tons of software, commercial or not, including full blown games like "Prince of Persia", was written in assembly, so again, yes....


> Or do you just punt and use ascii code pages?

You're assuming that there's even ASCII. colorForth doesn't use it. It's all memory, but what happens is that you write a set of words that operate on the memory to form the abstraction you need, such as Unicode.


On a new OS, forth often has a "first mover" advantage. E.g. some of the first apps for MacOS were developed in forth. It's also useful when limited resources are available. OO forth is quite pleasant to work in, if you really want to.

That said, I wish somebody would have created a statically typed forth.



I wish ... would have ... 20 years ago.


This can work, but it's incredibly tedious and not worth the trouble. Writing in Forth is like writing in pure CIL or JVM bytecode (they're both stack machines like Forth). Sure, you can do it, but if it was so rewarding, people wouldn't be using Java or C#.


That's not true at all. I know multiple people who are incredibly productive in Forth environments, though I don't use it myself. Pretending that it's like writing in JVM bytecode (despite some overlap between the two) is completely unfounded.

Your claim that C# and Java won based on merits is similarly unfounded: they won because they have major corporate backing. There are countless languages before and after that did what they did, better. Limbo beats Java at almost everything Java aims to do, for example.


I've met a couple of really good FORTH programmers over the years. They were humble about it, were also happy to use other tools in their work, and they just happened to be working on projects that were really good fits for FORTH's strengths.

I've met a lot more FORTH fanatics, people for whom FORTH was the answer no matter the problem; people who just would not shut up about how superior and fantastic it was, and have you seen the light, brother? Okay, most of them weren't that bad, but a few were (one guy I worked with got fired because (a) he told his boss that FORTH was all he was going to use, (b) the FORTH runtime was over half of his code budget and he had kinda been keeping that secret, and (c) none of that fantastically <insert some adjectives here> FORTH code was compatible with the ROM it needed to run on . . . so you can imagine that fun little tete a tete).

Whatever the languages's wins or faults, it's still a great idea to write your own FORTH at some point. It's a ton of fun, and quite instructive when you've gotten a fully functional programming environment that fits in a few tens of kilobytes.


> I've met a lot more FORTH fanatics, people for whom FORTH was the answer no matter the problem; people who just would not shut up about how superior and fantastic it was, and have you seen the light, brother?

I've seen exactly one Forth programmer like that, they are simply too rare. But the stereotype language fanatic is easily recognized in other language eco-systems, Perl and Rust have a disproportionately high share of this and I'm sure there are others. Typically this stems from either insecurity or a lack of exposure to other eco-systems or the drive to create camps of in and out groups to attempt to gain mindshare.

Very tiring and counterproductive in the long run, I think to some extent this is what killed Perl, simply that no matter how good the language was/is regular people simply don't want to be associated with fanatics and recognize that no tool will ever be perfect.


I think some of it may come from being forced to use X language, and then finding Y language, and realizing that it fits your mindset perfectly, and deciding that everyone using X must be idiots. Ie, these partisans were exposed to other languages, but never particularly liked them, until this new thing came along that seemed like a revelation. The zeal of a convert, sort of thing.

I haven't spent enough time with Forth or Rust to say, but I can see why both of them could seem like that to people. Forth, especially, has a sort of philosophical/hacker bent that is inherently appealing to a certain kind of person (including me!). Whereas I can't imagine Java ever winning someone over like that (though I'm sure it has).


I don't think it is major corporate backing -- for example, neither Perl nor PHP had one, and they still are (and always were) vastly more popular than Forth.

It seems to me that Forth only works for certain people, and those people are quite rare. For the majority of programmers, almost every other language will make them much more productive.


[flagged]


You can't attack another user like that on HN, no matter how strongly you feel or disagree. Please make your substantive points thoughtfully in the future.

https://news.ycombinator.com/newsguidelines.html


At a guess you've never actually done any significant programming in Forth.

The 777 wouldn't fly if you took out all the Forth code and there are plenty of other examples. Forth is at a much higher level than JVM byte code, byte code would be comparable to the output of a Forth compiler in roughly the same way that a .S file could be the output of a C compiler. Clearly C is at a (somewhat) higher level than Assembly.

Forth is definitely an acquired taste, but it is an interesting shift in perspective and as such a language that is worth learning, not because it will have a lot of commercial potential but because it makes you look at problems in a different way.

The same goes for LISP, Erlang/Elixir and Clojure.


It's not like pure CIL or JVM bytecode when the language can be extended. The process is to make what you need and only then write the program. It's remarkable how fast you can move up language levels once you grok it.


This is hard to believe, as in this thread and everywhere, the only software used to tout Forth is low-level stuff. Even if Forth was able to be squeezed into a high-level language, it doesn't seem to have any advantage, or, in fact any differences from existing ones. What would a high-level Forth bring to the table that Java or e.g. Clojure don't have?


Because Forth isn't really an application development language as much as it is a very flexible toolbox for programmers who are toolmakers themselves. You can write full applications in it but that is not where Forth shines. If I had to bring up a new machine though that would be one of the first things I would port. (And it would take a couple of hours at most once the hardware was up and running)


I feel like, from the "individualism" perspective, Forth is really a tool for hackers.

Hackers usually work alone or in very small groups, and the tools they need are usually handcrafted. They do not need corporate-level software, and long do they strive to be left alone, without being bothered.


What kind of projects are generally done by individuals even inside companies? I'm thinking maybe some simple device drivers but could be others.


Low-level hardware bringup?

Perhaps the reason this only ever ends up being managed by one or two people is that >2 people simply do not need to manage the fussy details once they've been abstracted/normalized out.

Also, I lurked in comp.lang.forth for a week or so a while back and learned that Mattel were using Forth as a runtime for their toys back in 2004, at least: https://groups.google.com/d/msg/comp.lang.forth/omi6PUvymEE/..., https://groups.google.com/d/msg/comp.lang.forth/LzOasFOyIMg/...


Thanks, I did not know the word bringup


Tons of projects. Even many well known today languages, frameworks, etc, started life as one man's initiative and pet project, which they worked on alone, inside a company.

But also tons of official projects in the enterprise. A company needs an X tool, Y is known for his capacity to deliver, they get assigned to make the tool.

Not every project requires a team.


Proofs of concept/early demos. Swift at Apple was the work of one person initially.


The classic problem with inventing your own language is that now you can't understand anyone else. If we're going to have widespread communication then we need widely-understood languages and libraries. The goal should be to replicate understanding. We need to evolve a common vocabulary rather than ending up with nearly as many private dialects as programmers. Publishing libraries and gaining users for them should be a primary goal.

There are Forth words with generally agreed-on definitions but it seems it's not designed to grow a large, common vocabulary in the way that modern package managers enable.


> Forth is quite "invidualistic", tailored to single persons or small groups of programmers, mostly because it is natural and relatively straightforward to implement a Forth system yourself (and you should) and because the language is inherently malleable and adapts to your needs. Therefore what I write is just one way of seeing Forth, which is both a programming language and a engineering philosophy.

Sometimes... you don't need to understand anyone else, or to be understood by anyone else. Sometimes you just want to do what you need to do.

The goal of replicating understanding is all well and good, but that shouldn't be the primary goal of programming language development, nor should it be the primary goal of programming.

I don't think that anyone is suggesting that we start rebuilding enterprise service frameworks in Forth.


As a specific example, I'm dabbling in embedded programming (like Arduino) and switched to Teensy for the audio library. I guess the Forth philosophy would be to figure out how to program real-time audio from the microprocessor's datasheet? I'm not really a fan of C++ but I knew better than to go that deep.

Arduino itself is a good example of how a decent, common abstraction can help users get started.


you could probably get a lot more done on arduino by writing a tiny forth in arduino C and then writing your applications in _that_


This seems to be the preferred methodology, actually. One of my close friends is an embedded systems developer and this is what he does.

One of the big advantages of Forth is that it's (apparently) pretty trivial to implement on your own, in whatever environment you choose.


The counterpoint is that every software project of reasonable complexity evolves its own language anyway.

When that is layered on top of overly generic libraries/frameworks, you may end up using only parts of the underlying vocabulary. Now the project is stuck with two vocabularies. Learning the generic one doesn't get you far and may be actively misleading ("yeah, we never use features X and Y of the framework, instead we've overridden Z with our own implementation that does all this plus W").


Yes, that's a problem with code sharing gone wrong. Another problem is framework churn where popular libraries aren't stable. When good, stable libraries aren't available then it may be better to reimplement than to use a bad one.

But these problems are, in their way, a sign of success at the package management level. If you don't have good package management then people probably aren't having much success at sharing code.

We should still aim for building and sharing stable, well-understood, high-quality code libraries. Unfortunately, sometimes the way to get there is to go through a period of instability.


> We should still aim for building and sharing stable, well-understood, high-quality code libraries.

And I agree with that 100% - but that isn't the solution to every single problem. That's all I'm saying.


I'm also for a common vocabulary, but I wonder why you're getting downvoated for having another opinion.


Then how can paradigm shifts occur?


[deleted]


Have you tried viewing it on mobile? It looks awful.


I would've agreed, but there are annoyingly arbitrary line breaks in the middle of sentences. A more minimalist approach would call for breaking only at the end of sentences, and letting the user's browser wrap in whichever way they preferred their window size + custom stylesheets + plugins to be


It is a text file. You are just complimenting the web server.


Ultimately, programming languages lie on a few spectra. Extremes are annoying, lack of access to them is sometimes too.

The big problem with the extremes especially the lower, is the amount of discipline they require from the user. If your team has anyone that ignores this, you get garbage and bugs, which get hard to fix as either the language constrains you due to structure or the problematic behaviors are depended upon transparently and this very hard to factor out.

Low level languages make it easier to commit the latter mistake. In higher level, this usually rears its ugly head as bad design where you have to replace big chunks of code at the same time, sometimes leaving compatibility glue. You cannot do that in some of the higher level "discipline" languages or have to hack it in nasty ways (hey Java and C# with reflection, C and C++ even with macros; try doing it in Haskell), while in something unstructured you're up a creek without a paddle...

On the upside, the DIY nature makes it harder to produce bad or any abstraction, as trying to write anything you do not understand ends poorly. The problem then with getting the debugger in is that this can let you code with incomplete understanding and depend on things that are not meant to be depended upon. Everything becomes an API. That accidental delay in revision x of hardware? Can't get rid of it. Used a slower bus in the past? Oops.

Defined interfaces are important too. Which is why VHDL and Verilog exist even in hardware world - and even they are on the soft side of defined.




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

Search: