It comes bundled with the C++ based JUCE GUI framework, SQLite, ODBC, encrypted binaries, JSON support, and a lot of support for other things that you typically don't see in a tool written by one person. The more expensive licenses come with AWS and automotive Linux support, so nifty for this area.
Sadly, it is closed source and commercial (although you can get source with an enterprise license I think). On the plus side, it is really cheap (there is a free version too I believe) and the main developer seems to add user requested features and fix bug reports very quickly.
I obviously wouldn't write a production system in it (although I'm sure you could), but I wouldn't mind having a fun REPL based language to write simple and easy to distribute cross-platform desktop apps.
"An elegant weapon for a more _civilized_ age"
It is the forth of choice for any serious forth developer.
I'm unlikely ever to learn forth, but if there are general lessons to be learnt from the mistakes made, I'll learn from them. So, what are those mistakes? (thanks)
Which one? Because Retro and ColorForth seem light-years apart to me.
High-level, what makes Forth interesting? What are it’s major strengths and weaknesses?
followed by a part written in FORTH: https://github.com/nornagon/jonesforth/blob/master/jonesfort...
It builds gradually from asm primitives into a somewhat sophisticated language with features like exceptions, function inlining and a decompiler.
The major strength, probably unique amongst programming languages, is that you can keep the entire system in your head (by "system", I mean both language and the simple operating system that it implements). The downside is that the code is write-only.
I've used that to build a FORTH interpreter on an AVR micro.
Small Forths can be totally understood by the programmer (perhaps just a few pages of code for their kernel), but still be expressive enough to actually use. Applications written in Forth are typically deeply entangled with the interpreter. You can't separate a "program" from the environment.
It's more like a collection of ideas and design principles than one singular language. Most Forth programmers write their own implementation, or several. You can decide what indulgences you want. (I like having "quotations"- inline anonymous word definitions like Retro has- and special-cased string literals. I also have my own style of looping words that aren't quite like ANS Forth.) This also means Forth code can never be dropped into another project and directly re-used: you can only take ideas.
Starting from a blank page every time, carrying with you ideas and writing code afresh is a good way to travel light. Self-reliance. Building human-scale programs. No one would write a web browser, or an Oracle-compatible SQL engine, in Forth, because a Forth programmer wouldn't have much of a use for those things. Solve only the problems you really have, with as little code as you can manage.
Forth is unique as it is probably the only serious language that you can write production systems in and understand the entire compiler/interpreter, parsers, data structures, and functions (called words).
Everything is pretty simple. You could use C as the base, but traditionally you would write a few dozen assembly routines and link them together and form some core words to manage a stack (there are 2 stacks and that is it) and make more complex words.
It gets a lot more deep, but Forths aren't really for me. If I'm going to use something weird, I'll write some APL.
APL/K and Forth also have very concise syntax for declaring functions.
const foo = (x,y) => 1 + x * y
// K, lambda with implicit arguments
// K, points-free
: foo * 1+ ;
I'm nowhere near your level of comprehension in either language, but agree that spending some time outside of curly brace land gets you more comfortable with other languages.
For some practical reason or just as an exercise?
In a stack based language those aren't interesting ideas, they're just naturally emergent.
The other thing is that it makes it utterly trivial to pull out or insert code in a function. allowing for some radical refactoring.
Strengths: Lisp-level metaprogramming facilities while also being extremely fast and close to the metal. Weakness: the Perl problem (write-only) but worse.
Like assembler, Forth is untyped.
These failings could be at least partly forgiven if Forth was fast. But it isn’t, because most words are short and all the stack manipulations are done with subroutine calls. It’s possible to write an inlining, optimizing compiler, but then the simple layout of code in memory is lost, and quick introspection using a memory dump becomes nearly impossible.
But, it is a neat tool to bootstrap a working system quickly on new, embedded hardware with limited resources. Definitely a black belt tool for lightweight systems written by a single person. Hard on maintenance programmers.
See Jonesforth for a tutorial example.
I wrote a Forth a few decades ago. The insight gained reminds me of Nand2tetris, but the latter is far more mainstream.
This can have an advantage: one can stuff much more code (and functionality) in a very memory limited system (e.g. smart cards), especially with WORDS compression:
"Huffman threaded code is one of the most compact representations known for a computer program"
Depends on who is the writer...
> There are also no local variables in standard Forth
Named locals variables are in the standard, as an optional extension. Because not everyone want them.
> you have to push (and pop!) them from the separate return stack
That's the main reason why the return stack is exposed to the programmer, yes, but there are other techniques like putting some of the data that are used throughout the program in (global) variables. you generally choose depending on how much simplification you gain and how much modularity you lose etc.
> Accessing parameters is done by manipulating the stack, which makes the code opaque after a year or so
Depends on how you write and comment.
> Named constants and variables are all global. So, it is very difficult for a team to use
Standard Forth as vocabularies to isolate functions, constants, and variables that is supposed to help with name clashes. Many variants like Retro have some sort of namespace mechanism.
Did you actually use Forth in a team context?
> These failings could be at least partly forgiven if Forth was fast. But it isn’t, because most words are short and all the stack manipulations are done with subroutine calls
Not a problem, because you design your programs to minimize stack juggling. Less overhead, more readable, one stone, two birds.
My dialect which uses very naive subroutine threading (it just executes lists of function pointers) can rival with Lua (non JIT) on certain tasks because my Forth doesn't spend its time doing useless stuff like looking up an hash table.
The speed of Forth is not in its bytecode interpreter. It's in the simplifications it encourages.
> It’s possible to write an inlining, optimizing compiler, but then the simple layout of code in memory is lost, and quick introspection using a memory dump becomes nearly impossible
I have written a few decades such a thing for the 8086, and I would say the lack of introspection was never a problem. When you write such a system, the hex machine codes sticks quickly in your head. I think I wrote a disassembler for the lulz, but an hex dump was probably my main "introspection" tool.
> I wrote a Forth a few decades ago
I wrote several Forth systems in the last few decades. The latest one I wrote is my goto-tool and serves me almost daily.
Unless working in assembly or something of that sort, tend to I comment only on noteworthy matters pertaining to specific logic, not recurring units of basic program structure and their routine connections.
This is no different from what people do with Doxygen. Nobody prevents anyone from using a Doxygen-like tool with Forth. Or even literate programming if that's your thing.
Inside definitions, if you have to insert a comment to remind yourself what the state of the stack is at that point, very often it means you have failed in some way. Some refactoring or rethinking is needed.
Nobody prevents anyone from writing a perfect solo project from scratch in which their idea of best practices is assiduously adhered to. Then there are projects where other people don't follow one's favorite practices, and projects one inherits that had previous maintainers which didn't follow those practices.
Except perhaps that you are more concerned with code quality when you know that it will be hard to read a couple of months later if you are sloppy.
Forth is unforgiving in many ways like an old master. There are people who call him a bad teacher, and there are people who listen to him. This is the difference between good students and bad students.
Anyway, the fact that Forth, Inc., has been in business for forty years and is still taking new commercial projects  is enough of a proof that Forth is more than just the code written by hobbyists you see on GitHub.
When we look at a clump of Forth code that's supposed to be a function, unless there is commentary, we don't know how many arguments it takes or how many values it leaves on the stack.
We don't have this problem with VBA, Java, Perl, C, Newlisp, Fortran or Python.
Forth is an assembly language for a pretty low level stack-based virtual machine. (And such machines are very popular, though rarely programmed by hand.)
It's not a good VM for compiling dynamic languages; it provides no run-time check on mismatched function arguments, and generating code to do that would be inefficient and incompatible with regular Forth function calls. (E.g. we can have a calling convention whereby we push the argument count that the callee will pop and check.)
Forth could be a good target for a static language where function calls are checked at compile time against declarations.
> Forth is unforgiving in many ways like an old master.
Forth is completely forgiving, like an old buffoon. Pass four arguments to a three-argument function? All is forgiven.
Forth is indeed an assembly language for Forth processors, but Forth was never intended as some sort of intermediate language. It is a programming language for humans since 1970.
It's interesting to note that, unlike Lisp machines and other language-X-processors who are now rusting in museums, "Forth processors" are still being launched in space .
So, to anticipated about what you say later, it must be a cosmic buffoon. Better than dying from a slightly cold winter I guess.
> It's not a good VM for compiling dynamic languages
Off topic, but why would you do that anyway? At some point, register-based VMs outperformed stack-based VMs due to the evolution of processors (cache, branch prediction, large register banks) and progresses in compilation techniques.
Forth doesn't need to be "recycled" as a target language. It is useful enough for those who've practiced it long enough to understand its value proposition.
> Forth is completely forgiving, like an old buffoon. Pass four arguments to a three-argument function? All is forgiven.
Lua and Newlisp (and probably other Lisp variants) let you pass more arguments or less arguments to a function; you won't get any warning while the compiler/interpreter silently ignores the extra parameters or fills the missing parameters with nil.
In Forth there are some ways to detect argument mismatches, but really nobody cares about those training wheels. The problem is solved in a different way.
Also, many dynamic languages had to add a "strict mode" because letting people not declare identifiers is not so "user-friendly" after all...
I'll trade any day a language that breaks on stupid typos for a language that breaks because I forgot to push a result. Actually, that's why I did.
ANSI Common Lisp and Scheme do not allow too many or too few arguments to be passed to a function.
Let's try CLISP:
> (defun test () (cons 1 2 3))
> (compile 'test)
WARNING: in TEST : CONS was called with 3 arguments, but it requires 2
> (defun test (x))
> (compile 'test)
WARNING: in TEST : variable X is not used.
Misspelled or missing IGNORE declaration
> (defun test (x) (+ x y))
> (compile 'test)
WARNING: in TEST : Y is neither declared nor bound,
it will be treated as if it were declared SPECIAL.
Error checking is for lesser programmers: very effective advocacy!
> The problem is solved in a different way
Exhaustive testing? The problem is that there isn't even a reliable dynamic check. A misuse of the data stack in one place can affect code elsewhere. Just because you step on the bug doesn't mean that the machine will halt with a diagnostic, let alone one that pinpoints the cause.
Well, your opinion about it has changed quite a bit... I still like it probably because I am less interested in "serious about Lisp" than in pragmatic solutions. The other day I shipped a small networking utility as two files: the standard newlisp.exe and a newlisp script. I used Newlisp instead of my equivalent written in Forth because the user may have needed to hack it a little bit. The instruction I had to give was "just copy that on your desktop and drag the script file to the exe file". Job: done. I count it as Lisp being useful in 2019.
Anyway, there's still dozens of scripting languages that won't move an eyebrow about bad arity.
>> nobody cares about those training wheels.
> Error checking is for lesser programmers: very effective advocacy!
Beginners are not lesser programmers, they are less skilled programmers. It's a temporary condition.
Also, I'm not advocating Forth. It hasn't caught in 40 years, so it is obviously not for everyone. I'm just clearing what I think is misunderstandings. Just like I would defend a show when I think some reviewer totally misunderstood it.
> Exhaustive testing?
Yes, this is one of the ways. Whether you use static, dynamic, or no type-checking that's something you do anyway when things get serious. Because none of them catch all bugs.
> The problem is that there isn't even a reliable dynamic check
Stack imbalances in practice catch half of the programming errors. It's a bit like in Haskell: if it compiles, it has good chances to be right. In Forth, it translates into: if the depth of the stack doesn't change in your main loop, the program is probably right.
> A misuse of the data stack in one place can affect code elsewhere.
Most often the program just segfaults right away, because you're manipulating pointers quite often. That's another 25% of errors caught.
> doesn't mean that the machine will halt with a diagnostic, let alone one that pinpoints the cause
Relying on stack dumps and debuggers is a terrible habit. Looking for the simplest way to solve a problem is far more effective globally.
I know this sounds insane. I was there decades ago. I built a Forth that couldn't possibly crash and was disappointed by the result. I built Forth debuggers and was also disappointed. I built support debugging primitives in VMs and I was disappointed. That's because I was trying to cure the symptoms instead of the illness. Then I started to throw away and rewrite when a piece of code was buggy. That helped a lot.
For one thing you get rid quickly of the "lets try this to see if it works" mentality, which is a recipe for bugs and half-baked code. "Do. Or do not. There is no try".
If debugging is the process of removing software bugs, then programming must be the process of putting them in.
-- Edsger Dijkstra
This guy was right. Studies have shown that the amount of bugs is proportional to the LOC count. The message is "Program less, think more", and this is also what Forth says:
You factor. You factor, you factor, you factor and you throw away everything that isn't being used, that isn't justified.
-- Chuck Moore, 1x Forth 
Basically that's "The simplest thing that can possibly work"  approach taken to the extreme. The thing is, being extreme is not easy. You can pick a low-hanging fruit or two by being audacious sometimes, but being extreme means climbing the tree to the top.
I'm not aware that I have held any other opinion about NewLisp at any time in the past.
The language described by ANS Forth not only doesn't have arity checking, it has no type checking. That is to say, the items on the data stack have no type: not run-time, not static. Two addresses could be pushed onto the stack, and then operated on as a double integer.
There is nothing wrong with that, just like there is nothing wrong with machine language instruction sets. We can write a compiler for a higher level language which compiles to ANS Forth, one that is considerably safe. There is value in that because we still have the benefit of portability, small run-time footprint and low resource use, depending on our application size.
Type, arity and other checking is not analogous to training wheels for beginners. Nobody believes nonsense like this once they are out of their programming puberty. I suspect you wouldn't say that in an interview, if you actually wanted the job.
> Most often the program just segfaults right away
If you're getting a "segfault" it's because you're running on a virtual memory system (likely written for you in a considerable amount of C).
Things don't always segfault right away. The problem can be in a conditional code that depends on particular kinds of inputs.
If you're having to trivial function arity mismatches with segfaults (when you have the luxury segfaults) is ridiculous, you can't simultaneously believe that you're working in a higher level language on par with Common Lisp or Scheme, or even ANSI C.
> Anyway, there's still dozens of scripting languages that won't move an eyebrow about bad arity.
And they are all garbage; so what? Many of these languages, however, will at least behave somewhat safely in the face of bad arity. That is to say, if there are insufficient arguments, those parameters get some default values, and excess arguments are ignored, without lingering around to cause a problem elsewhere.
You are better off targeting some bytecode VM rather than targeting the Forth language itself. You can target for instance Lua Bytecode and still have all of the benefits.
> Type, arity and other checking is not analogous to training wheels for beginners. Nobody believes nonsense like this once they are out of their programming puberty. I suspect you wouldn't say that in an interview, if you actually wanted the job.
Well I used to believe it was nonsense too. Maybe I'm older that you. Or maybe I was more curious.
I would definitely mention the fact I can program in a point-free, type-less language. That's an additional skill, not a hindrance. I didn't have the chance to test this because I already have a programming job (where I use Forth for personal supporting tools that help dealing with the peculiarities of embedded systems or with the shortcomings of our official software - but don't tell anyone). What I wouldn't do is trying to sell something like Forth to an unknown team for an unknown project.
> If you're getting a "segfault" it's because you're running on a virtual memory system (likely written for you in a considerable amount of C).
Actually yes, it assumes the processor has at least a MMU (virtual memory technically is an additional feature that's often built on MMU). If the CPU doesn't have a MMU, the virtual machine can perform some checks on the pointers (e.g. memory alignment if the CPU requires it). At my current level of sloppiness that's something that could be helpful if really I can't fix my bad habits.
> If you're having [segfaults because of] trivial function arity mismatch, you can't simultaneously believe that you're working in a higher level language on par with Common Lisp or Scheme, or even ANSI C.
I think I did say that Forth is indeed a low-level language. I have no problem with that, because "low-level language" doesn't mean "lesser language" to me.
You could use lambda notation to name the parameters of a function, while still preserving postfix function application as in FORTH. This would effectively give you De Bruijn notation, in which named lambda was often denoted `[var] expr`. Though, ironically, De Bruijn is also known for his "nameless" De Bruijn indexes, which dispense with the very naming of function parameters, replacing these names with what are in effect references to positions on some abstract argument stack.
Not even close. Rather more like macro-assembler-level metaprogramming facilities.
Ultimately a language is there to make programming a CPU easier. If you believe in just absolute minimal complexity, FORTH is what you get when you find this intersection of leverage vs. minimal tolerable complexity. A FORTHer generally doesn't believe in writing device drivers so much as writing maybe 20 lines of code yourself that twiddle the registers to get done what you want. You're basically told to ask yourself over and over again "do you really need that?" The answer is usually "well I might need it tomorrow... maybe..." but in software, requirements change so fast that the abstractions we design for what we think we might need tomorrow don't actually match what's actually needed. The FORTHer solution isn't to just continue generalizing until you have a fully galaxy brain interface that could accommodate anything the future could throw at you-- it's just to not generalize at all, and to put the weight of working in a system with less abstraction on the programmer. The bet is that the weight of dealing with less abstraction is less weight than dealing with a tower of abstractions that were built for the wrong requirements.
I don't know if I fully buy it, but it's worth trying to think like a FORTHer as an intellectual exercise to make you a better programmer.
In a typical non-Forth "stack VM", there is a single stack which behaves like the C stack: allocate an activation record with locals on a function call, pop the whole thing off at the end. Intermediate calculations go on top of all this, avoiding the need to explicitly register allocate in bytecode, but operands don't naturally flow between function definitions; they're copied around explicitly as the stack grows and shrinks.