Forth Vs Lisp

An even more interesting argument than EmacsVsVi. In ForthLanguage, the model is a stack machine and the programmer manipulates this stack, with data and words being pushed on the stack. ForthWords? are essentially denotations of code. In LispLanguage, the model is one of composing functions, but given its facilities to manipulate lists and to represent code as lists, the comparison of ForthLanguage and LispLanguage includes the manipulation of ForthWords? on the stack and LispLanguage functions as lists.

The two are not necessarily mutually exclusive. There are some Forth's out there that have Lambda and all sorts of other features.

Lambdas, in Forth? I've never seen one -- which ones have that?

[While it's not Forth, check out JoyLanguage]

Yes, coincidentally, I did, just last week, in detail. Very interesting language.

Anyway, yeah, discuss :)

--MikeGodfrey

There is also ConseptizerLanguage?, which is stack-based, but even uses EssExpressions - it's kind of a stack-based Lisp.


I'm surprised no-one here has yet mentioned FactorLanguage. It really is the union of Forth and Lisp, which is to say, a practical implementation of Joy with full CommonLisp -type libraries. --NateCull?


I'd like to offer a correction to the above statement. Lisp emphasizes the application of functions. Juxtaposition of syntactical elements specifies arguments. Forth (like all other ConcatenativeLanguages) treats juxtaposition as proper composition. In this light, Forth is much, much closer to Backus' FP than Lisp is. Example in Lisp:

  (f (g (h ...)))

in Forth:

  h g f

I should point out that neither way is better nor worse; I see them as equal. Personally, I like both.

--SamuelFalvo?


[deleted misunderstanding]

Lisp is much closer to other dynamic languages like Python, Dylan and so on. You would not naively compare these to Forth, because they don't have a "weird" syntax. Lisp is a block structured language with lexical scope, like Algol;

Lisp has had dynamic scoping since its invention; lexical scoping was added as the default halfway through its history because it proved to reduce some classes of programming errors dramatically. But dynamic scoping was never deleted; see the "special" keyword.

it's closer to Algol than to Forth, even. It has no implied stack architecture;

Sure it does. In fact, most languages that support recursive functions have an implied stack. The strongest difference is that Forth explicitly (not implicitly!) uses a stack for evaluation, not just for control. Now that is a big difference. In Lisp (and C and in many languages), the mechanism used for passing parameters, returning values from functions, and holding temporary results is invisible, while in Forth it is part of the language definition to use a stack for all that.

it's really a platform-independent HighLevelLanguage. A Lisp function can be compiled to a native machine-language subroutine with fairly conventional calling conventions.

Untrue. Having support for the lexical scoping you mentioned means that, when a function is dynamically created, care must be taken to make sure that all variables that it can access remain alive semi-permanently, even if the function was created by another function which later returned. This is an implementation headache that e.g. C does not face. Continuations are also a big issue that are far from "fairly conventional calling conventions".

A Lisp program doesn't have to worry about cleaning up a call stack properly, or reserving space for local variables, etc - there are no "stack bugs". The computational model is quite abstract: there is no concrete, low level, standard-defined mechanism such as a stack by which data flows from one expression to another.

This much at least seems true.

Don't let the parentheses fool you: that's just an extra feature that allows for convenient source-to-source transformations. Rather than parsing some ad-hoc syntax like Pascal, C or Algol, straight to a syntax tree that is hidden from the programmer, Lisp takes a layered approach: parse a simple syntax to structured data, let the programmer have a chance to mangle it, and then parse the structured data as the program source.

Forth on the other hand is probably best regarded as a kind of assembly language for an abstract, stack-based instruction set architecture. The string of words that makes up a Forth program is essentially a sequence of opcodes and pseudo-ops. A Forth interpreter can be implemented in a very small space, because it is basically an emulator of a simple CPU.

Yep. Which is a cool thing about Forth. But you misunderstand a lot about Lisp.

In fact, Forth's creator ChuckMoore went on to design many Forth CPU's, each simpler, smaller, and faster than the one before.


In ForthLanguage, you are expected to act as the compiler, or more abstractly an ExtensionOfYourComputer?.

Forth allows one to have low-level control over how compilation works, but the above statement doesn't make sense. Forth truly lets one use the computer as an ExtensionOfYourMind?, whereas Lisp hides the computer behind a set of mathematical abstractions. Additional layers of abstractions are not always helpful. I've always found LambdaClauses? and other esoteric features of Lisp to be mental barriers. (But I'll admit I'm an idiot; SmugLispWeenies can feel free to criticize my lack of intelligence.) -- KrisJohnson

In a sense, Forth lets you program the computer, and Lisp turns it into a party game where you have to program the computer in a blindfold. Truly a hard art to master, but if you can, and can see programming from both sides of the curtain, you'll be a greater programmer for it. -- JohnAllsup

I am currently reading the 7th Chapter of "Let Over Lambda" by Doug Hoyte. While I would agree that, in general, Lisp (at least, Common Lisp) allows you to program with a blindfold, the point of Chapter 7 is that you could get as close to the metal as you would like in Lisp. Having said that, I'm interested in Forth because, while you tend to start close to the metal, it is my understanding that you could get pretty far away from it if you so desire. In this sense, I consider both to be "transcendental" of what we have normally come to expect of computer languages! (I should also add that I'm looking forward to Chapter 8: "Lisp Moving Forth Moving Lisp". It's bound to be interesting!) -- Alpheus


Lisp has a syntax by which one writes a piece of data representing a tree, and that whole thing is then subject to evaluation (data becomes code). That evaluation may do some transformation on the tree, if some of the operators are macros.

The Forth language has no such structural syntax. A Forth program is just a list of words. But that there is a structure is undeniable; that structure is what Forth programmers indicate using indentation.

The problem is that the parsing of that structure is up to the Forth program itself. There are no parentheses to show where something starts and where it ends. Looking at a Forth word, you have no idea how much stack it will consume.

The Forth code

  A B C D
could be analogous to any of these Lisp expressions, and in addition, it could leave stuff on the stack, or cause a stack underflow:
 (D A B C)

(D (C A B))

(D (C (B A)))

(D (C (B (A))))
You just don't know, without knowing the semantics of the words, what the real syntax is. It could be that A, B and C each just leave one value on the stack, and D is a triadic function, etc.

Forth has a stack notation which is a convention for annotating a function definition to show how it transforms the stack. The problem is you have to go to the definition to see it; it's not apparent at the point of a word's use.

As a Forth program evaluates, it essentially parses that syntax. The evaluation stack doubles as a parsing stack; the stack transformations are very similar to the actions of a stack-based shift-reduce parser. For example

  1 2 +
is very much like a grammar production: shift 1, shift 2, reduce by the + grammar rule, with the associated action of performing addition.

This offloading and distribution of responsibilities from the language into the code is the key to the success of Forth: it allows very tiny, fast interpreters to be written. They don't have to do very much at all! But this stack management means that Forth is a low-level language; in a higher-level language, programmers expect implementation entities such as stacks to be managed for them. You don't want to know how many words to pop off from a stack after a function is done: that is considered a calling convention that is done away with by the function abstraction. Let the compiler implement it in the best way for the given hardware: registers, register windows, one or more stacks, caller cleans, callee cleans, etc.

Forth certainly starts out as a low-level language; however, as you define additional words, the level of abstraction increases arbitrarily. There are several points which you raise that are patently and demonstrably false:

--SamuelFalvo?

Forth programs can be manually transformed by moving groups of words around, which Forth programmers think is some kind of huge convenience. The problem is that such transformations must take care to preserve the stack level. If you take some group of words which, as a collective, consume five stack values and produce three, you can only plant that group into a matching context, which prepares at least five values on the stack, and consumes three afterward. This is nothing like the tree transformations of Lisp macros.

False; if you use a tree transformation in Lisp that returns a list, in the context of code which expects an atom, you're in for a run-time surprise. You're ascribing a problem to Forth which exists everywhere. --SamuelFalvo?


I learned code factoring from Forth. I have no experience with Lisp. Forth encourages one to simplify. Occasionally this can lead to an unhealthy terseness (making it possible to write Forth that would make ObfuscatedCee blush).

Once a Forth programmer gets past the fad stage (I went through it, too) and realizes that it's a GoodThing to be able to maintain your own code, then a reasonable compromise between compactness and clarity (hopefully) happens.

When I code in C, I use a RuleOfThumb that if a given routine takes more than a single page of paper to print, I'm probably doing something wrong. Doesn't always work, but it's a good guideline. "Traditional" Forth, because it uses "screens" (16 rows x 64 cols = 1k block) of storage as the default edit size, tends to encourage a "one screen per routine" bias.

I can't say I agree that I ever felt like I was "being the compiler" when factoring my code. But then, I drive a car with a stick shift, and I don't feel like I'm the transmission, either.

Unhappily, I've never actually worked with Forth in a paid environment. Employers have been much more interested in other, more conventional languages, so my Forth programming happens in fits and starts as time allows.

I have an interest in Lisp, but have never gotten past the "gonna read that book" stage. (AndyDent suggests play with the LogoLanguage if you want to experiment with recursive, list-oriented languages)

-- GarryHamilton


Are they really that different? OK, visually they're very different, but if you put that aside they're both interactive, interpreted/compiled, object-oriented languages with built-in facilities for extending the language (syntactically). You can think of either as a "build your own language toolkit", and in both you work by building the language up to your problem domain. They are primarily different in the range of problems that can be tackled quickly with the built-in features.

My two cents.

-- EricSessoms?

How are they object oriented? I mean, CLOS adds object orientation to Lisp, sure. But object-oriented in general?

-- Riki

I think it would be better to say that both are "object-orientable". That is, if you are stuck with a Lisp or a Forth without syntax to help with object orientation, it's not all that difficult to add it. Contrast this with certain AssemblyLanguages and CeeLanguage. Technically, you can program object-oriented software in these languages (or even in a structured-language style in Assembler), but you have to do it by hand, and there is no way to extend the syntax to support object-oriented programming without creating an entirely new compiler (which is what CeePlusPlus and ObjectiveCee attempt to do)...

--Alpheus


The big difference is that Forth is a functional language only when dealing with simple, memory-sized values, whereas Lisp extends this to arbitrary data objects. Writing some functions in Forth is simple and beautiful:

 : square dup * ;
but things get much messier when you have to start dealing with memory. Try writing a Forth word to arbitrarily add two three element vectors, for example. In Lisp you don't run up against the same wall. Whether or not this is a problem depends on how you view Forth.

-- JamesHague


Generalized to arbitrary-length arrays, and using one array as an accumulator, I more or less naturally come up with:

 : ADD-CELLS ( addr addr count -- )
   0 DO
     OVER I CELLS + OVER I CELLS + @ OVER @ + SWAP !
   LOOP DROP DROP ;

\ alternatively: (doesn't look that much simpler, but consider: there are just 6 words, rather than 14, within the loop construct) : add-cells ( addr1 addr2 n -- ) cells bounds do dup @ i +! cell+ cell +loop drop ;

\ invocation would be: addr1 addr2 10 add-cells

Aside from the stack swizzling (which confirms [no. it doesn't, it shows that the problem hasn't been sufficiently simplified] the "you are the compiler" observation from above), this is similar to the C version:

 void add_cells(int *a, int *b, int count) {
   int i;

for (i = 0; i < count; i++) a[i] += b[i]; }

Common Lisp, of course, provides most of this behavior for free:

 (defun add-cells (a b)
   (map-into a #'+ a b))

You could write the combinators to permit the same thing in Forth easily enough (and in C not so easily). The Forth would then look like

  ' + A B 3 BINMAP

and the C like

  mapbinary(twerpy_plus_function, a, b, 3);

C gets pretty crude at this point, because of its distinction between functions and operators, but the Forth is about as clean as the Lisp, and the syntax is surprisingly similar.

   --BillTrost, mostly, I think, despite lots of careless whacking at the text

There is a huge difference between having to remind the language how big your array is by including that little 3, and not having to do that.

depends on the representation of your arrays. you're free to choose, and might want to choose for arrays with built-in length and/or population information. in fact, Forth does not know ARRAY - you're expected to add your flavour of arrays to the language. Often, Forth programmers don't choose for "built-in" parameters, as it wrestles away control from the programmer. In the Forth examples above, no array has been passed to ADD-CELLS, but memory addresses, without any array size information related to those. In the lisp example, that size has been determined beforehand, by declaring and populating the arrays. I have implemeted APL-style matrix operations in Forth at one point. Those allowed me to do:

 A B +!
( the +! was resolved against a vocabulary of APL style words, used instead of the Forth vocabulary integer +!, when the operands were APL-style scalars or matrici ). ( +! is read as plus-store )


In Forth you can "assign" code to data with DOES>. Thus, you can keep size of array together with array data, and let array put it on the stack each time it's accessed.

 : array create , dup cells allot does> dup cell+ swap @ ;
Array creation:

 100 array my-array
After executing my-array there will be size of array and array address on the stack.


In Common Lisp:

   (map-into a #'+ a b)

Which generalizes to any number of arguments also, which is a more fundamental difference

   (map-into a #'+ a b c d e f g)


The GForth tutorial has some extremely cool example of writing a map-array in Forth. The first version takes an execution token, just like the BINMAP example. Then, a version is written which compiles the code for the map construct, given the execution token at compile time. Cool!

http://www.complang.tuwien.ac.at/forth/gforth/Docs-html/Advanced-macros-Tutorial.html#Advanced%20macros%20Tutorial

See also ForthMacro.


Nice example! The major difference is still that you have to manage memory yourself in Forth (where does the array come from?) whereas in Lisp you don't care. You can manipulate a number or a list or an array with the same relative ease. In Forth there is a harsh division between cell-sized types that fit on the stack and memory-based data structures. You certainly *can* wrap up data structure manipulations inside of your own words, but that doesn't eliminate the fundamental difference. Once something doesn't fit on the stack, you have to give much more thought to your solution.

True enough. This would only be a problem for me when pointers to objects don't fit on the stack. As long as the pointer is stack-sized, I can pedal along nicely. Using 32-bit pointers against a 16-bit stack (or 64 on 32) gives me gas.


Yes, that is clearly a major difference. Consider e.g. that Forth has different words for floating point manipulation. One of the cool aspects of Lisp is that "generic" routines such as map can be written in it, since every value can be represented using only one cell. For this to be possible, garbage collection is essential. But even if we grant this major difference, it is surprising how much MetaProgramming Forth allows. In that respect, it is very similar to Lisp.

I note that all variants of the ML programming language also have distinct operators for integer versus floating point versus ... . It is, in fact, a requirement to do type inferencing.

Untrue. Haskell does just fine with the same operators for all numeric types.


Hey, um, shouldn't this page have a big banner? You know, something like

TONIGHT!! FORTH VERSUS LISP!! CAGE GRUDGE MATCH OF THE <appropriate time period>!! TWO LANGUAGES ENTER!! ONE LANGUAGE LEAVES!!

No.

Aww, no fun :-).

  Lisper: "Obviously, all right-thinking functions should be on the left:
       multiply must be prefix!"
  Forther: "Heresy! Functions should be on the right,
      multiply must be postfix!"
  C/Java Onlookers: "Wha...? Both of you guys are weird, 
      infix multiply is the only way to go!"
  Lisper and Forther, in unison: "Kill the blasphemers!"

Star Trek episode "Let That Be Your Last Battlefield"
  Bele: It is obvious to the most simple-minded that Lokai is
     of an inferior breed.
  Spock: The obvious visual evidence, Commissioner, is that he
     is of the same breed as yourself.
  Bele: Are you blind, Commander Spock? Well, look at me. Look at me!
  Kirk: You are black on one side and white on the other.
  Bele: I am black on the right side.
  Spock: I fail to see the significant difference.
  Bele: Lokai is white on the right side. All of his people are white
     on the right side.

Bele: And we like to hang around in tights.

...with apologies from DougMerritt :-)

Shouldn't they give reason for why they prefer prefix or postfix or infix? I like postfix and the reason is because: First you have to compute the value of what is being multiplied, and then you have to tell it to multiply those two numbers together. Right? It makes sense to me!


How about JoyLanguage? If anything is a cross between Forth and Lisp, that's it.

Joy unfortunately hasn't managed to be more than a very slow and feature-thin toy. Some functional syntax words for forth might be more rewarding, though I haven't seen anything close to it. A shame, since there's so many OOF implementations, you'd think FP would be the next area of interest.

I got sidetracked, but nearly a year ago I was playing around with putting functional stuff into Forth based on Joy and Euphoria. If anyone wants to look at it I have the work in progress here: [http://member.netlink.com.au/~peterl/furphy.html] - PML.

Recently, someone played around with adding a Haskell-ish syntax to Forth. The results are on http://wiki.forthfreak.net/index.cgi?FunForth

How functional do you want to go? Consider the CATCH word: it takes as a parameter an XT to execute (execution token, like a ticked Lisp word I think) and returns 0 or the exception thrown. One could use this mechanism to write MAP or FOLD on any data structure you wish. You could also define a word to iterate over the datatype if you want a completely generic MAP function. The Forth way is to write your words as you need them. For instance, in my Forth chess program, I wrote a board mapper forEachSq used as follows: ' generateMove forEachSq. That was a nice abstraction that allowed me to play around with different iteration schemes without touching the various functions that needed to scan the board. But maybe this is just programming in a functional style, as opposed to using a functional language (sorta like using an object style in C (such as the FILE * library functions) doesn't make C an object language).

Another strategy I've seen in Forth is to extend the compiler to create a generative solution to the problem (though I'm not sure that is really functional). -- IanOsgood


I only just came across this discussion from the Forth webring. I've been using Forth for 25 years and am author of ROBOFORTH (strobotics.com/robofort.htm). I can see the discussion is long over but would add an adage that I came across many years ago: "The good news is you can do anything with Forth, the bad news is you will probably have to do it yourself." -- David Sands

Nice adage. Interviews with ChuckMoore make it exceedingly clear that he thinks that is a good thing, which is doubtless one part of why it continues to be true. So much of the world agrees that reinventing the wheel is a bad idea, that this is kind of a mind-boggling attitude, but eventually I sort of got his point, although I think his approach is suitable only for certain niches, and his extremism on that (and fighting against standards rather than helping them along) is not generally a great thing.

BTW discussions here are never over, let alone "long over" -- they just get paused for a long time sometimes. ;-) -- DougMerritt


Echoing some sentiments here: the biggest difference results from the approach to types and memory management. Forth style results in a smaller, simpler (and usually faster) implementation, making it ideal for the build-from-scratch paradigm ChuckMoore emphasizes. Lisp OTOH can build upon the type system to further extend the language in ways that Forth finds difficult. A major hurdle for both, when coming from an imperative background, is the emphasis on minimizing the use of named and scoped variables in favor of stack-swizzles, car/cdr/cons, or recursion.


CategoryProgrammingLanguageComparisons


EditText of this page (last edited May 30, 2014) or FindPage with title or text search