Blog Games GitHub Twitter Mastodon
[ up a level ]

2022-07-20 16:00

The many wrongs of concatenative.org

Concatenative programming has been doing the rounds lately. Unfortunately, in their proselytising of concatenative programming, the wonderful writers at concatenative.org are doing functional programming a disservice in a number of ways, and I think its time to set the record straight. Since the site is a wiki, Ill use quotes to present the site as it is now. Note that as a Haskell practicioner I will be using Haskell as the counterpoint to most of the wrongs of concatenative.org.

In short: In traditional functional programming languages derived from the lambda calculus, putting two symbols next to each other (like f x) represents applying the function f to the argument x. This will be familiar to users of bash, which uses the same format for running commands.

In contrast, a concatenative (functional) programming language is a language where putting two symbols next to each other represents function composition. Taking Factor as a typical concatenative language, f g would represent the composed function which runs f and passes the result through g. In Haskell we would write this as \x -> g (f x), or equivalently g . f.


Concatenative Language

Lets start with the front page that explains what a concatenative language is. We begin at the very first sentence:

There are many ways to categorize programming languages; one is to define them as either concatenative or applicative. In an applicative language, things are evaluated by applying functions to arguments. [&]

Things are evaluated by applying functions to arguments in any language that has functions. This includes concatenative programming languages. There is no semantic difference here; the thing that formally separates concatenative from applicative programming is syntax. Grouping together Python, Haskell and Java (as the page goes on to do) in this sense is not meaningful. By any sensible measure, programming in Haskell is much more like programming in Factor or Forth than programming in Java.

Moving on:

Any language that allows recursive definitions uses some type of call stack to save return addresses between function calls, [&]

This is false as a matter of simple fact. Haskell uses a graph reduction style approach to evaluate expressions, despite having fully fledged recursion. While GHC (the sainted Haskell compiler) can simulate a call stack, this is a courtesy for the programmer rather than part of the evaluation procedure.

Most languages in widespread use today are applicative languages: the central construct in the language is some form of function call, where a function is applied to a set of parameters, where each parameter is itself the result of a function call, the name of a variable, or a constant.

The central construct in a language like Javascript, Python, etc. is not the function call. The closest thing imperative/procedural programming has to a central is sequencing, which is typically invisible to the programmer but indicated by a line break or a semicolon, or alternatively the statement, which is some kind of operation over mutable state such as assignment. Most languages in widespread use today are procedural in this sense, usually defined in opposition to functional languages, including Haskell and Factor. Applicative vs concatentive is the wrong line to draw.

Concatenation is Composition

This page did the rounds on the Orange Website last week, so lots of people will have read it read comments about it by now. But I didnt see these points in particular being called out.

Now, this is a website that evangelises concatenative programming and I am not going to call out the fact that they are cherry picking examples. Its perfectly reasonable that they show off the times when concatenative programming works out well! However, this page boasts the following example:

Compare these three quotations,

[ even? ]
[ even? not ]
[ 4 > ]

This is shorter than the equivalent in an applicative language, because we have to name the input argument, that is only used once. In Common Lisp:

[&]

In Haskell:

(even)
(not . even)
(4 >)

The total number of characters is the same length in Haskell excluding whitespace, and shorter if you include whitespace. Thats probably not the example you wanted to go with. At least its correctthough of course, we could write even instead of (even), in which case Haskell comes out on top.

Name Code not Values

On the third page of our travels we encounter the main problem with this site and the reason I am ranting at length about it: The authors seem to not know how to write non-Factor functional code well, and are using that as a stick to beat other functional languages with.

[To be continued&]

[ up a level ]