The Big Mud Puddle - Why Concatenative Programming Matters

Download as pdf or txt
Download as pdf or txt
You are on page 1of 46

The Big Mud Puddle

Jon Purdy writes blarticles on computing, language, and design.

Home ▼

12 February 2012

Why Concatenative Programming Matters


Introduction

There doesn’t seem to be a good tutorial out there for concatenative programming, so I
figured I’d write one, inspired by the classic “Why Functional Programming Matters” by
John Hughes. With any luck it will get more people interested in the topic, and give me a
URL to hand people when they ask what the heck I’m so excited about.

Foremost, there seems to be some disagreement over what the term “concatenative”
actually means. This Stack Overflow answer by Norman Ramsey even goes so far as to
say:

“…it is not useful to use the word ‘concatenative’ to describe programming


languages. This area appears to be a private playground for Manfred von
Thun. There is no real definition of what constitutes a concatenative language,
and there is no mature theory underlying the idea of a concatenative
language.”

This is rather harsh, and, well, wrong. Not entirely wrong, mind, just rather wrong. But it’s
not surprising that this kind of misinformation gets around, because concatenative
programming isn’t all that well known. (I aim to change that.)

Concatenative programming is so called because it uses function composition instead


of function application—a non-concatenative language is thus called applicative. That’s
the defining difference, and it’s as “real” a definition as I need, because, well, it’s the one
that people are using. Bang. Descriptivism.

One of the problems with functional programming is that the oft-touted advantages—
immutability, referential transparency, mathematical purity, &c.—don’t immediately seem
to apply in the real world. The reason “Why Functional Programming Matters” was
necessary in the first place was that functional programming had been mischaracterised
:
as a paradigm of negatives—no mutation, no side-effects—so everybody knew what you
couldn’t do, but few people grasped what you could.
There is a similar problem with concatenative programming. Just look at the Wikipedia
introduction:

A concatenative programming language is a point-free programming language


in which all expressions denote functions and the juxtaposition of expressions
denotes function composition. The combination of a compositional semantics
with a syntax that mirrors such a semantics makes concatenative languages
highly amenable to algebraic manipulation.

This is all true—and all irrelevant to your immediate problem of why you should care. So
in the next sections, I will show you:
How concatenative programming works

How typing in a concatenative language works

How concatenative languages are efficient

What concatenative languages are not good for

Where to go for more information

Now let’s get started!

♦♦♦

The Basics

In an applicative language, functions are applied to values to get other values. λ-calculus,
the basis of functional languages, formalises application as “β-reduction”, which just says
that if you have a function (f x = x + 1) then you can substitute a call to that function (f y)
with its result (y + 1). In λ-calculus, simple juxtaposition (f x) denotes application, but
function composition must be handled with an explicit composition function:

compose := λf. λg. λx. f (g x)

This definition says “the composition (compose) of two functions (f and g) is the result of
applying one (f) to the result of the other (g x)”, which is pretty much a literal definition.
Note that this function can only be used to compose functions of a single argument—
more on this later.

In concatenative languages, composition is implicit: “f g” is the composition of f and


g. However, this does not mean that function application becomes explicit—it actually
becomes unnecessary. And as it turns out, this peculiar fact makes these languages a
whole lot simpler to build, use, and reason about.
:
The untyped λ-calculus is a relatively simple system. However, it and the many systems
derived from it still need three kinds of term—variables, lambdas, and applications—as
well as a number of rules about how to correctly replace variable names when applying
functions. You have to deal with name binding, closures, and scope. For a supposedly
low-level language, it has quite a bit of inherent complexity.

Concatenative languages have a much simpler basis—there are only functions and
compositions, and evaluation is just the simplification of functions. It is never necessary
to deal with named state—there are no variables. In a sense, concatenative languages
are “more functional” than traditional functional languages! And yet, as we will see, they
are also easy to implement efficiently.

♦♦♦

Composition

Let’s say we want to multiply a couple of numbers. In a typical concatenative language,


this looks like:

23×

There are two weird things about this.


First, we use postfix notation (2 3 ×) rather than the prefix notation (× 2 3) that is common
in the functional world, or the infix notation (2 × 3) that most languages provide for
convenience. There is nothing stopping a concatenative language from having infix
operators, but for the sake of consistency, most stick to postfix notation: “f g” means
(g ∘ f), i.e., the reverse of function composition. This is actually rather nice notation,
because it means that data flows in the order the functions are written in.

Second, we said all terms denote functions—so what the heck are 2 and 3 doing in there?
They sure look like values to me! But if you tilt your head a little, you can see them as
functions too: values take no arguments and return themselves. If we were to write
down the inputs and outputs of these functions, it would look like this:

2 :: () → (int)
3 :: () → (int)

As you may guess, “x :: T” means “x is of type T”, and “T1 → T2” means “a function from
type T1 to type T2”. So these functions take no input, and return one integer. We also
know the type of the multiplication function, which takes two integers and returns just one:

× :: (int, int) → (int)

Now how do you compose all of these functions together? Remember we said “f g”
:
means the (reverse) composition of f and g, but how can we compose 2 with 3 when their
inputs and outputs don’t match up? You can’t pass an integer to a function that takes no
arguments.

The solution lies in something called stack polymorphism. Basically, we can give a
generic, polymorphic type to these functions that says they’ll take any input, followed by
what they actually need. They return the arguments they don’t use, followed by an actual
return value:

2 :: ∀A. (A) → (A, int)


3 :: ∀A. (A) → (A, int)
× :: ∀A. (A, int, int) → (A, int)

“∀A.” means “For all A”—in these examples, even if A has commas in it. So now the
meaning of the expression “2 3” is clear: it is a function that takes no input and returns
both 2 and 3. This works because when we compose two functions, we match up the
output of one with the input of the other, so we start with the following definitions:

2 :: ∀A. (A) → (A, int)


3 :: ∀B. (B) → (B, int)

We match up the types:

(A, int) = (B)

By substituting, we get a new polymorphic type for 3 within the expression:

3 :: ∀C. (C, int) → (C, int, int)

This matches the non-polymorphic type:

3 :: ∀A. (A, int) → (A, int, int) = ∀B. B → (B, int)

So the final type of the expression becomes:

2 3 :: ∀A. (A) → (A, int, int)

Going through the same process for multiplication, we get:

2 3 :: ∀A. (A) → (A, int, int)


× :: ∀B. (B, int, int) → (B, int)
A=B
2 3 × :: ∀A. (A) → (A, int)

This is correct: the expression “2 3 ×” takes no input and produces one integer. Whew! As
a sanity check, note also that the equivalent function “6” has the same type as “2 3 ×”:

6 :: ∀A. (A) → (A, int)


:
So already, concatenative languages give us something applicative functional languages
generally can’t: we can actually return multiple values from a function, not just
tuples. And thanks to stack polymorphism, we have a uniform way to compose functions
of different types, so the flow of data in our programs doesn’t get obscured, as it were, by
the plumbing.

♦♦♦

Cool Stuff

In the above example, we worked from left to right, but because composition is
associative, you can actually do it in any order. In math terms, (f ∘ g) ∘ h = f ∘ (g ∘ h). Just
as “2 3 ×” contains “2 3”, a function returning two integers, it also contains “3 ×”, a
function that returns thrice its argument:

3 × :: (int) → (int)

(From now on I’ll omit the ∀ bit from type signatures to make them easier to read.)

So we can already trivially represent partial function application. But this is actually a
huge win in another way. Applicative languages need to have a defined associativity for
function application (almost always from left to right), but here we’re free from this
restriction. A compiler for a statically typed concatenative language could literally:

1. Divide the program into arbitrary segments


2. Compile every segment in parallel
3. Compose all the segments at the end

This is impossible to do with any other type of language. With concatenative


programming, a parallel compiler is a plain old map-reduce!

Because all we have are functions and composition, a concatenative program is a single
function—typically an impure one with side effects, but that’s by no means a requirement.
(You can conceive of a pure, lazy concatenative language with side-effect management
along the lines of Haskell.) Because a program is just a function, you can think about
composing programs in the same way.

This is the basic reason Unix pipes are so powerful: they form a rudimentary string-
based concatenative programming language. You can send the output of one program to
another (|); send, receive, and redirect multiple I/O streams (n<, 2&>1); and more. At the
end of the day, a concatenative program is all about the flow of data from start to finish.
And that again is why concatenative composition is written in “reverse” order—because
it’s actually forward:

+---+
:
| 2 |
+---+
|
| +---+
| | 3 |
| +---+
| |
V V
+---------+
| * |
+---------+
|
V

♦♦♦

Implementation

So far, I have deliberately stuck to high-level terms that pertain to all concatenative
languages, without any details about how they’re actually implemented. One of the very
cool things about concatenative languages is that while they are inherently quite
functional, they also have a very straightforward and efficient imperative implementation.
In fact, concatenative languages are the basis of many things you use every day:
The Java Virtual Machine on your PC and mobile phone

The CPython bytecode interpreter that powers BitTorrent, Dropbox, and YouTube

The PostScript page description language that runs many of the world’s printers

The Forth language that started it all, which still enjoys popularity on embedded
systems
The type of a concatenative function is formulated so that it takes any number of inputs,
uses only the topmost of these, and returns the unused input followed by actual output.
These functions are essentially operating on a list-like data structure, one that allows
removal and insertion only at one end. And any programmer worth his salt can tell you
what that structure is called.

It’s a stack. Yep. Just a stack.

Consider the information flowing between functions in the expression “2 3 × 4 5 × +”,


which, if you’re not up on your postfix, is equal to (2 × 3 + 4 × 5):

Function Output
()
2 (2)
:
3 (2, 3)
× (6)
4 (6, 4)
5 (6, 4, 5)
× (6, 20)
+ (26)

Moving from left to right in the expression, whenever we encounter a “value” (remember:
a nullary self-returning function), we push its result to the stack. Whenever we encounter
an “operator” (a non-nullary function), we pop its arguments, perform the computation,
and push its result. Another name for postfix is reverse Polish notation, which achieved
great success in the calculator market on every HP calculator sold between 1968 and
1977—and many thereafter.

So a concatenative language is a functional language that is not only easy, but trivial to
run efficiently, so much so that most language VMs are essentially concatenative. x86
relies heavily on a stack for local state, so even C programs have a little bit of
concatenativity in ’em, even though x86 machines are register-based.

Furthermore, it’s straightforward to make some very clever optimisations, which are
ultimately based on simple pattern matching and replacement. The Factor compiler uses
these principles to produce very efficient code. The JVM and CPython VMs, being stack-
based, are also in the business of executing and optimising concatenative languages, so
the paradigm is far from unresearched. In fact, a sizable portion of all the compiler
optimisation research that has ever been done has involved virtual stack machines.

♦♦♦

Point-free Expressions

It is considered good functional programming style to write functions in point-free form,


omitting the unnecessary mention of variables (points) on which the function operates.
For example, “f x y = x + y” can be written as “f = (+)”. It is clearer and less repetitious to
say “f is the addition function” than “f returns the sum of its two arguments”.

More importantly, it’s more meaningful to write what a function is versus what it does, and
point-free functions are more succinct than so-called “pointful” ones. For all these
reasons, point-free style is generally considered a Good Thing™.

However, if functional programmers really believe that point-free style is ideal, they
shouldn’t be using applicative languages! Let’s say you want to write a function that tells
you the number of elements in a list that satisfy a predicate. In Haskell, for instance:
:
countWhere :: (a -> Bool) -> [a] -> Int
countWhere predicate list = length (filter predicate list)

It’s pretty simple, even if you’re not so familiar with Haskell. countWhere returns the length
of the list you get when you filter out elements of a list that don’t satisfy a predicate.
Now we can use it like so:

countWhere (>2) [1, 2, 3, 4, 5] == 3

We can write this a couple of ways in point-free style, omitting predicate and list:

countWhere = (length .) . filter


countWhere = (.) (.) (.) length filter

But the meaning of the weird repeated self-application of the composition operator (.)
isn’t necessarily obvious. The expression (.) (.) (.)—equivalently (.) . (.) using infix
syntax—represents a function that composes a unary function (length) with a binary
function (filter). This type of composition is occasionally written .:, with the type you
might expect:

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d


(.:) = (.) . (.)

countWhere = length .: filter

But what are we really doing here? In an applicative language, we have to jump through
some hoops in order to get the basic concatenative operators we want, and get them to
typecheck. When implementing composition in terms of application, we must explicitly
implement every type of composition.

In particular, there is no uniform way to compose functions of different numbers of


arguments or results. To even get close to that in Haskell, you have to use the curry and
uncurry functions to explicitly wrap things up into tuples. No matter what, you need
different combinators to compose functions of different types, because Haskell doesn’t
have stack polymorphism for function arguments, and it inherently can’t.

Writing point-free expressions demands concatenative semantics, which just aren’t


natural in an applicative language. Now contrast a concatenative example:

define countWhere [filter length]


(1, 2, 3, 4, 5) [2 >] countWhere
:
It’s almost painfully literal: “to count the number of elements in a list that match a
predicate is to filter it and take the length”. In a concatenative language, there is no need
at all for variables, because all you’re doing is building a machine for data to flow through:

+-----------------+
| (1, 2, 3, 4, 5) |
+-----------------+
|
| +-------+
| | [2 >] |
| +-------+
| |
+---|-----------------|-----+
| | countWhere | |
| V V |
| +-----------------------+ |
| | filter | |
| +-----------------------+ |
| | |
| V |
| +--------+ |
| | length | |
| +--------+ |
| | |
+---|-----------------------+
|
V

When you’re building a diagram like this, just follow a few simple rules:

1. Each block is one function


2. A block takes up as many columns as needed by its inputs or outputs, whichever
are more
3. When adding a block, put it in the rightmost column possible:
If it takes no inputs, add a column

If there aren’t enough arrows to match the block, the program is ill-typed

♦♦♦

Quotations

Notice the use of brackets for the predicate [2 >] in the preceding example? In addition to
composition, the feature that completes a concatenative language is quotation, which
allows deferred composition of functions. For example, “2 >” is a function that returns
whether its argument is greater than 2, and [2 >] is a function that returns “2 >”.
:
It’s at this point that we go meta. While just composition lets us build descriptions of
dataflow machines, quotation lets us build machines that operate on descriptions of other
machines. Quotation eliminates the distinction between code and data, in a simple, type-
safe manner.

The “filter” machine mentioned earlier takes in the blueprints for a machine that accepts
list values and returns Booleans, and filters a list according to the instructions in those
blueprints. Here’s the type signature for it:

filter :: (list T, T → bool) → (list T)

There are all kinds of things you can do with this. You can write a function that applies a
quotation to some arguments, without knowing what those arguments are:

apply :: ∀A B. (A, A → B) → (B)

You can write a function to compose two quotations into a new one:

compose :: ∀A B C. (A → B, B → C) → (A → C)

And you can write one to convert a function to a quotation that returns it:

quote :: (T) → (() → T)

♦♦♦

The Dark Side

Say you want to convert a math expression into concatenative form:

f x y z = y2 + x2 − |y|

This has a bit of everything: it mentions one of its inputs multiple times, the order of the
variables in the expression doesn’t match the order of the inputs, and one of the inputs is
ignored. So we need a function that gives us an extra copy of a value:

dup :: (T) → (T, T)

A function that lets us reorder our arguments:

swap :: (T1, T2) → (T2, T1)

And a function that lets us ignore an argument:

drop :: (T) → ()

From the basic functions we’ve defined so far, we can make some other useful functions,
:
such as one that joins two values into a quotation:

join2 :: (T1, T2) → (() → (T1, T2))


join2 = quote swap quote swap compose

Or a function that rotates its three arguments leftward:

rot3 :: (T1, T2, T3) → (T2, T3, T1)


rot3 = join2 swap quote compose apply

And hey, thanks to quotations, it’s also easy to declare your own control structures:

define true [[drop apply]] # Apply the first of two arguments.


define false [[swap drop apply]] # Apply the second of two arguments.
define if [apply] # Apply a Boolean to two quotations.

And using them is like—actually, just is—using a regular function:

["2 is still less than 3."] # "Then" branch.


["Oops, we must be in space."] # "Else" branch.
2 3 < # Condition.
if print # Print the resulting string.

Those particular definitions for true and false will be familiar to anyone who’s used
Booleans in the λ-calculus. A Boolean is a quotation, so it behaves like an ordinary value,
but it contains a binary function that chooses one branch and discards another. “If-then-
else” is merely the application of that quotation to the particular branches.

Anyway, getting back to the math, we already know the type of our function ((int, int, int)
→ (int)), we just need to deduce how to get there. If we build a diagram of how the data
flows through the expression, we might get this:

+---+ +---+ +---+


| x | | y | | z |
+---+ +---+ +---+
x y z
| | |
| | V
| | +------+
| | | drop |
| | +------+
| V
| +----------+
| | dup |
| +----------+
| y y
| | |
:
| | V
| | +----------+
| | | dup |
| | +----------+
| | y y
| | | |
| | V V
| | +----------+
| | | * |
| | +----------+
| | y^2
| | |
| V V
| +----------+
| | swap |
| +----------+
| y^2 y
| | |
| | V
| | +-----+
| | | abs |
| | +-----+
| | |y|
| | |
V V V
+-----------------+
| rot3 |
+-----------------+
y^2 |y| x
| | |
| | V
| | +----------+
| | | dup |
| | +----------+
| | x x
| | | |
| | V V
| | +----------+
| | | * |
| | +----------+
| | x^2
| | |
| V V
| +----------+
| | swap |
| +----------+
| x^2 |y|
| | |
| V V
| +----------+
| | - |
| +----------+
| x^2-|y|
| |
V V
+----------+
| + |
:
+----------+
y^2+x^2-|y|
|
V

So our final function can be written:

f = drop dup dup × swap abs rot3 dup × swap − +

Well…that sucked.

♦♦♦

A Lighter Note

You’ve just seen one of the major problems with concatenative programming—hey, every
kind of language has its strengths and weaknesses, but most language designers will lie
to you about the latter. Writing seemingly simple math expressions can be difficult
and unintuitive, especially using just the functions we’ve seen so far. To do so exposes
all of the underlying complexity of the expression that we’re accustomed to deferring to a
compiler.

Factor gets around this by introducing a facility for lexically scoped local variables. Some
things are simply more natural to write with named state rather than a bunch of stack-
shuffling. However, the vast majority of programs are not predominated by mathematical
expressions, so in practice this feature is not used very much:

“Out of 38,088 word and method definitions in the source code of Factor and
its development environment at the time of this writing, 310 were defined with
named parameters.”—Factor: A Dynamic Stack-based Programming
Language

One of the great strengths of concatenative languages, however, is their ability to refactor
complex expressions. Because every sequence of terms is just a function, you can
directly pull out commonly used code into its own function, without even needing to
rewrite anything. There are generally no variables to rename, nor state to manage.

So in practice, a lot of expressions can be refactored using a wide array of handy


functions, of the sort that commonly end up in a standard library. With some refactoring,
that math expression might look like this:

square = dup ×
f = drop [square] [abs] bi − [square] dip +

Which doesn’t look so bad, and actually reads pretty well: the difference between the
square and absolute value of the second argument, plus the square of the first. But even
that description shows that our mathematical language has evolved as inherently
:
applicative. It’s better sometimes just to stick to tradition.

♦♦♦

Whither Hence

So you’ve got the gist, and it only took a few dozen mentions of the word “concatenative”
to get there. I hope you’re not suffering from semantic satiation.

You’ve seen that concatenative programming is a paradigm like any other, with a real
definition and its own pros and cons:

Concatenative languages are simple and consistent (“everything is a”)

They are amenable to dataflow programming

Stack languages are well studied and have good performance

You can easily roll your own control structures

Everything is written in point-free style (for better or worse)

If you’re interested in trying out a mature, practical concatenative language, check out
Factor and the official blog of the creator, Slava Pestov. Also see Cat for more information
on static typing in concatenative languages.

I’ve been idly working on a little concatenative language called Kitten off and on for a
while now. It’s dynamically typed and compiles to C, so you can run it just about
anywhere. I wanted a language I could use for a site on a shared host where installing
compilers was irritating. That shows you the extent of my language geekery—I’d rather
spend hours writing a language than twenty minutes figuring out how to install GHC on
Bluehost.

Anyway, the implementation is just barely complete enough to play around with. Feel free
to browse the source, try it out, and offer feedback. You’ll need GHC and GCC to build it,
and I imagine it works best on Linux or Unix, but there shouldn’t be any particularly
horrible incompatibilities.

This would also be a good time to mention that I’m working on a more serious language
called Magnet, which I mentioned in my last article about how programming is borked. It’s
principally concatenative, but also has applicative syntax for convenience, and relies
heavily on the twin magics of pattern matching and generalised algebraic data types. Hell,
half the reason I wrote this article was to provide background for Magnet. So expect more
articles about that.

Edit (20 April 2013) The above information is no longer accurate. Kitten is currently being
:
rewritten; the work that was done on Magnet has been incorporated. Kitten is now
statically typed and, until the compiler is complete, interpreted.

And that about does it. As always, feel free to email me at evincarofautumn@gmail.com
to talk at length about anything. Happy coding!

Jon Purdy at 12:02 AM

Share

112 comments:

Jef February 12, 2012 at 1:05 PM


What is the difference between concatenative language and FP-systems ?
http://c2.com/cgi/wiki?CanProgrammingBeLiberatedFromTheVonNeumannStyle
Reply

Replies

Jon Purdy February 12, 2012 at 1:28 PM


Concatenative languages are an elaboration on and simplification of FP systems;
some of the distinctions that FP systems maintain are arbitrary, such as between
objects and functions. Also, row polymorphism makes the definition of a complete
concatenative system much cleaner and smaller; you can get away with only two
stack combinators (see http://tunes.org/~iepos/joy.html), and it’s possible to give a
type to the Y combinator (∀A B. (A, (A, (A → B)) → B) → B).

Reply

chad wackerman February 12, 2012 at 1:32 PM


Nice article! Maybe mention RPN and the Forth language for sake of completeness?
Reply

Replies

Jon Purdy February 12, 2012 at 1:49 PM


Thanks. Have added a couple of notes.

Reply
:
João Neto February 12, 2012 at 2:16 PM
Great Post! Thanks for writing it.
Reply

Jesse February 12, 2012 at 3:33 PM


Cool stuff.
Reply

Twan van Laarhoven February 12, 2012 at 3:35 PM


Just curious: is there a fundamental reason why you can't use lambda's and variables in a
concatenative language?

For example, the 'function' \x could pop the top value of the stack, and bind it to the name x for
the remainder of the expression. Then

f = \z \y \x y square x square + y abs -

The reverse order of popping the arguments could be a bit confusing, but I would say that this
is a clear improvement.
Reply

Replies

Jon Purdy February 12, 2012 at 4:18 PM


Indeed, there is no particular reason—Factor allows something similar based on
stack annotations, and Prog will do it with pattern matching. The “remainder of the
expression” bit is a little unclear; you would obviously need to delimit the definition
somehow (even if just with a newline) lest scopes run amok. I would prefer a syntax
like “\ z y x { y square x square + y abs ‒ }”, but the principle is the same. Manfred
von Thun shows in “The Theory of Concatenative Combinators”
(http://tunes.org/~iepos/joy.html) that you can use stack combinators to rewrite any
expression with lambdas into an expression without them. But my attitude is like
yours: “can” doesn’t necessarily mean “should”.

mndrix February 15, 2012 at 10:40 AM


"The Theory of Concatenative Combinators" is written by Brent Kerby not Manfred
von Thun

Reply
:
ceii February 12, 2012 at 4:28 PM
Many thanks for writing this! I'm finally starting to feel like being excited about concatenative
languages is something I'm allowed to be proud of as a functional programmer. I'll have to take
a long look at Prog's design; I've always wondered how pattern matching could be combined
with concatenative syntax in a sensible and convenient way.

One major advantage which you didn't mention and which doesn't seem to have been explored
a lot yet: a (statically typed) concatenative language can afford incredible IDE support. The
type of the stack at the cursor's position is everything you need to know about your current
context; just display it in a corner and the programmer has all the info they need. Better yet,
autocomplete for words that can take the current stack as input and you get IntelliSense on
steroids.
Reply

Replies

wtanksley February 13, 2012 at 11:21 AM


ceii, I agree. I'd also like to see dynamic dataflow, with the IDE drawing a dataflow
graph centered around the word nearest the cursor, showing you where its data
came from and is going to.

Marc Coram February 24, 2012 at 4:04 AM


That sounds cool. I'd love to see a prototype. Maybe you could select an interval of
code and it would graph the flow associated with that fragment. Sounds like
something a capable person could draft in html5 quickly (or perhaps factor's IDE?).
(I disagree that types are "all you need". (int,int,int) isn't informative enough about
what is what, for example, if you've been rolling and duping and dipping and
dropping lately). Actually on that count, the data flow graph could *be* the language
with the right UI.

Reply

Justin February 13, 2012 at 12:16 AM


Really great article. Thanks.

I am writing a compiler that targets CIL right now. Thinking of all this in terms of a stack really
made it so much easier to follow. .NET is not completely stack based though as it uses local
variables as well.
Reply
:
Anonymous February 13, 2012 at 3:35 AM
If you'd just called it a 'stack based language' instead of concatenate it'd all have been
massively clearer from the start..

Also, you list the Android phone as having a stack-based interpreter. In fact its famously
register based, which is why it is so much faster. Its not a JVM, its Dalvik.
http://en.wikipedia.org/wiki/Dalvik_%28software%29#Architecture
Reply

Replies

wtanksley February 13, 2012 at 12:00 PM


I admit that the article started out a bit unclear in its purpose, but you have to admit
he cleared up any confusion quite well. (And considering that he's starting with
Wikipedia's definition, he's doing GREAT in comparison.)

You're right about Dalvik being register based, but I can't find anything suggesting
that it was done for speed, unless it was actually done to give reasonable speed
without a JIT (and whether that's true or not, I can find no hint that any research was
done). Lua's switch to a register machine is always described in terms of a speed-
centered choice, but it's very clear that they were comparing a modern optimized
register machine versus an antique naive stack machine; not a fair comparison at
all.

A JITted virtual stack machine should be faster on arbitrary CPUs than a register
machine -- the only exception being when the virtual register machine is a very close
match to the physical register machine (after the overhead of virtualization).

Jon Purdy February 13, 2012 at 12:16 PM


Thanks for the information. I removed the Android reference.

The reason I structured this how I did is that the paradigm is not bound to stacks.
There are a lot of high-level concepts that apply to all concatenative languages
whether they use a stack or not. I wanted to demonstrate those concepts first,
before showing that a stack is just an efficient way of implementing them.

It’s like Smalltalk and JavaScript versus Simula and C++: even though the former
pair has prototypes and messages while the latter has classes and methods, they’re
both object-oriented. A concatenative language based on term rewriting or directed
graphs would still be concatenative, so long as it were still based on composition
and quotation.

dmm February 20, 2012 at 2:22 PM


:
Did you mean Self instead of Smalltalk? Self has prototypes and messages (and
methods but not classes.) Smalltalk has classes and methods (and messages but
not prototypes.)

Reply

Anonymous February 13, 2012 at 4:20 AM


"The PostScript page description language that runs most of the world’s printers"

"Most of the world's printers" are either cheap-ass inkjets or industrial thermal printers, neither
of which use PostScript. Even the vast majority of mid-range business printers are non-
PostScript.

In fact, you'd be hard-pressed to find a PostScript printer that was made in the last 10 years
and isn't a high-end network laser printer.
Reply

Replies

Jon Purdy February 13, 2012 at 12:00 PM


Thanks for the information. Edited.

Reply

Matt jones February 13, 2012 at 10:13 AM


One quick note on the HP calculator front: they were sold WAY later than 1977, and some of
us still love our HP48s. :)

However, I do wonder about the usefulness of this style for longer programs - I wrote quite a
few medium-sized things on the HP48, and the code was always *intensely* write-only since
you had to have a mental model of exactly what was on the stack where to even hope to follow
it. Not sure how much of that was due to the system discouraging the definition of many small
functions, though (you could only edit one function at a time).
Reply

Replies

Jon Purdy February 13, 2012 at 12:40 PM


Thanks, I didn’t know that about the calculators. I’ll correct that.

Whether it’s useful for larger programs I think just depends on the programmer and
:
the program. Concatenative programming demands obsessive factoring. If you don’t
have “many small functions”, you’re doing it wrong, and your work will be harder
than it has to be.

It’s like writing assembly. You can think about everything at the instruction level, with
a mental model (or excessive comments) about what’s where. But you’ll never get
anything done that way, because you’re working at the wrong level of abstraction. If
you’re shuffling the stack a lot, then you’re treating the symptom of a problem you’re
not seeing because you need to step back.

Concatenative languages are metalanguages. (Indeed, something I neglected to talk


about is how good they are for metaprogramming.) You use the language to create
structures that are suited to your problem, so you can solve the problem naturally.
That’s what you ought to do in any language, but concatenative programming
makes it almost painfully direct. Some like it, others don’t.

Unknown February 13, 2012 at 1:27 PM


You use the language to create structures that are suited to your problem, so you
can solve the problem naturally. That’s what you ought to do in any language, but
concatenative programming makes it almost painfully direct. Some like it, others
don’t.

Put "functional programming" in there and the statement still works.

Anonymous February 13, 2012 at 2:32 PM


One problem with concatenative languages and point free style is, i think, that
named variables are some kind of documentation.

With a concatenative lanuguage, you need to know (or document in an informal


comment) what inputs and outputs a function has. And in which order it's on the
stack.

Jon Purdy February 13, 2012 at 5:09 PM


“…named variables are some kind of documentation.”

True, but not always. The name of a parameter is often immaterial or generic; most
of the identifiers in programs I’ve read have been largely meaningless syntactic junk.
Where names serve a real purpose is in math expressions, where you expect to do
symbolic manipulation.

“With a concatenative lanuguage, you need to know (or document in an informal


comment) what inputs and outputs a function has. And in which order…”
:
That’s equally true of any dynamically typed language.

Reply

namekuseijin February 13, 2012 at 4:41 PM


I've read the original John Hughes paper and this was an interesting read as well.

That said:
"Concatenative programming is so called because it uses function composition instead of
function application—a non-concatenative language is thus called applicative."

I must observe: at some point, you need to apply your composed functions to your arguments
to get any results at all.

No, that "5 is a function!" talk is just a silly fallacy to make it look overly-sophisticated what is
really just values in a stack consumed by subsequent function calls. I can say the same for any
scheme expression: (+ 1 2 3 4 5) is not then the application of the arguments to the function +,
but the composition of a new function out of + and constant "functions" denoted by numbers... I
can then compose another function from this new function (15) and get yet another "function":
(* 2 (+ 1 2 3 4 5))

"the expression “2 3 ×” takes no input and produces one integer" please...

"concatenative languages give us something applicative functional languages generally can’t:


we can actually return multiple values from a function, not just tuples"

No, you can't. You can just fill a stack and let subsequent calls alter it.

In scheme you may return multiple values, but the context must be waiting for them. (values 1
2 3) actually returns 3 values instead of a tuple (list). In the top-level REPL, it returns the
separate 3 values, but if you were to use them, you'd better make a context to consume them:

(call-with-values
(lambda () (values 1 2 3)) ; return multiple values
(lambda (x y z) (+ x y z))) ; bind the values to the arguments x y z

In other words: (+ 1 (sqrt 4)) won't return 2 values, even if sqrt returned 2 values (default sqrt in
scheme returns just the positive value) because its continuation (+ 1 []) expects a single value.

say:
> (define (sqrt2 n)
(let ((pos (sqrt n))) ; original sqrt
(values pos (* -1 pos))))
> (sqrt2 4)
:
2
-2
> (+ 1 (sqrt2 4))
context expected 1 value, received 2 values: 2 -2

OTOH, this would work, but is too much of a hassle:

(call-with-values
(lambda () (sqrt2 4)) ; assuming it returns 2 values
(lambda (pos neg) (values (+ 1 pos) (+ 1 neg)))) ; returns 2 values too

it's much simpler to just return lists, which are Lisp's forte anyway, just like stacks are for
concatenative languages. So you just return a list and use the usual functional higher-order
functions to process the result:

> (define (sqrt2 n)


(let ((r (sqrt n)))
(list r (* -1 n))))
> (sqrt2 25)
'(5 -25)
> (map (lambda (x) (+ 1 x)) (sqrt2 25))
'(6 -24)

All that said, concatenative languages always sound to me like the exact opposite of Lisp as
far as syntax is concerned. :)
Reply

Replies

wtanksley February 14, 2012 at 5:29 PM


"I must observe: at some point, you need to apply your composed functions to your
arguments to get any results at all."

Which textual functions compose with which textual arguments? The answer isn't as
cut and dried as you imply -- remember, concatenation (and composition) is
associative. In an applicative language the answer is VERY clear in the text.

"No, that "5 is a function!" talk is just a silly fallacy"

Ouch.

"to make it look overly-sophisticated what is really just values in a stack consumed
by subsequent function calls."

You're assuming stack semantics. The author explained that textual evaluation is
another possible semantics; there's no "really just values" there. I believe
:
www.enchiladacode.nl is a purely rewriting semantics (although that may just be my
bad memory). Apter built a few concatenative languages using things like a stack-
dequeue pair. Most languages entirely WITHOUT a stack are mere toys, but I'm not
sure it will always be so. Oh, and of course, all professional Forths aside from stuff
for hardware stack machines perform register optimizations, which uses a stack
approximately like a C program would.

You're also missing the point. In the _language_, that is in the mapping of text to
semantics, there is no difference between a literal and a function. The semantics of
a literal may be simple; but then again they may not. The point is that a literal like "3"
is the same sort of language (text) object as a function; it's parsed the same way
(even though obviously it's lexed differently).

For higher-order languages, and for real lower-level languages, not all symbols are
so tidy. There has to be a symbol that means "do not execute the following
symbols", which is NOT semantically similar to what a function does.

"I can say the same for any scheme expression: (+ 1 2 3 4 5) is not then the
application of the arguments to the function +, but the composition of a new function
out of + and constant "functions" denoted by numbers... I can then compose another
function from this new function (15) and get yet another "function": (* 2 (+ 1 2 3 4
5))"

You're not performing composition; you're building a tree, not a string. It's not
associative except between siblings on the tree (and that's not a concept that's
directly apparent in the text; you have to convert it to a parsed data structure to see
which things are siblings).

"please..."

Well... It's true. And what's more, because of the associative property, _every
lexically correct substring_ has a valid type, and can be pulled out and made a
function. (Of course, this is only true inasfar as the language is purely concatenative.
All currently used languages offer things like definitions and nested blocks, which
are not associative. I made a language which doesn't have any of those and is
therefore purely concatenative, but you wouldn't want to code in it; its name is
"zeroone", and its two functions are named "zero" and "one". The bits, not the
English numerals.)

"All that said, concatenative languages always sound to me like the exact opposite
of Lisp as far as syntax is concerned. :)"

Sounds fair to me :-). And no parens (unless you want literal lists or quotations).

-Wm
:
Codingtales February 15, 2012 at 1:21 PM
"No, that "5 is a function!" talk is just a silly fallacy to make it look overly-
sophisticated"

Understand the difference between ($ 5) and (5) in Haskell. 5 in Concat languages


is equivalent to ($ 5) in Haskell. It's not overly-sophisticated talk, it's actually useful
and I've used it several times in Haskell.

Codingtales February 15, 2012 at 1:23 PM


Sorry, Blogger messed it up. I meant `dollar` 5

Jon Purdy February 15, 2012 at 1:33 PM


@Codingtales

That was MathJax actually, which I’ve since disabled.

Reply

Anonymous February 13, 2012 at 8:33 PM


Your Haskell examples are a bit unfair, I think: you omitted the style it would most likely be
written in:

> countWhere predicate = length . filter predicate


Reply

Replies

Jon Purdy February 14, 2012 at 10:45 AM


Oh, absolutely. They’re deliberately unfair—I was showing that point-free style is not
as natural in applicative style as in concatenative.

Reply

Toby February 14, 2012 at 12:45 AM


I always thought that concatenative languages were a bit inelegant due to the apparently
explicit stack. You know, it's point-free but there's a stack there to help you out, so whatever. :)

But having the semantics formalised via row polymorphism actually makes the stack an
:
implementation detail and the elegance has returned!

Thanks for a great article.


Reply

Replies

Jon Purdy February 14, 2012 at 11:16 AM


Thanks. Yeah, I’d say stacks are relevant, but not fundamental. Indeed, Prog will
use a stack for storage, but all word simplification will be done by pattern matching
and rewriting, so it’s not inherently a stack language.

Reply

Torbjørn Marø February 14, 2012 at 7:20 AM


Very clear and informative article. Thanks! I experimented with both Forth and Factor a couple
of months ago, and found it quite interesting how the languages made me start to think about
algorithms.

Forth also prompted me to try Rebol, which I totally enjoyed. I'm starting to see how ideas
evolve and change from language to language, and I think knowing this is really valuable for
any serious developer.
Reply

John Zabroski February 14, 2012 at 2:59 PM


I believe the principle reason virtual machines have recently moved away from stack machine
representations was the cost in maintaining invariants over the stack representation. The
overhead exceeded the benefits

However, for a peephole optimization, a stack-based representation might be most optimal.

Cheers,
Z-Bo
Reply

Replies

wtanksley February 16, 2012 at 3:01 PM


It is indeed easy to peephole optimize stack-based representation. In particular,
naive register allocation is very easy; if your datum is near the top of the stack it's
FAR more likely to be a good candidate for register allocation than a datum deeper
:
down. In essence, the time the programmer spends trying to write code without too
many "SWAP"s and other "stack noise" leads not only to clear code, but also to
prioritized dataflow. (In practice it's always harder.)

Now, I don't understand what you mean by "maintaining invariants". What invariants
are you talking about? Is there some context here that I'm missing?

-Wm

Reply

smitty1e February 14, 2012 at 3:56 PM


I thought this post was going to be an executable m4 macro, i.e., string concatenation. ;-)
Reply

Glen February 14, 2012 at 6:57 PM


This is the coolest thing ever. It brings new insight in how to implement USL/DBTF
(http://en.wikipedia.org/wiki/Universal_Systems_Language).
Reply

Kallikanzarid February 15, 2012 at 8:55 AM


I can't see any substance in this: you're just replacing dealing with explicit elements to dealing
with morphisms () -> T, which is how elements are *defined* in CCCs.
Reply

Unknown February 15, 2012 at 10:36 AM


Math Nerd Stuff!
Reply

Kallikanzarid February 15, 2012 at 1:37 PM


"In math terms, (f ∘ g) ∘ h = f ∘ (g ∘ h). Just as “2 3 ×” contains “2 3”, a function returning two
integers, it also contains “3 ×”, a function that returns thrice its argument" - this is sloppy, the
two obviously differ by currying, which is something we cannot forget "in math terms".
Reply

Replies
:
Jon Purdy February 15, 2012 at 8:59 PM
It would be, except I established early on how row-polymorphic functions are
uniformly composable. Currying is not at play.

Reply

Dylan Mikus February 15, 2012 at 2:20 PM


I have a question about the use of the compose function in Join2. I was just working through
examples by hand to make sure I understood the dynamics of the language, and, given "0 1
join2", I ended up at a step:

(() -> 0) (() -> 1) ((A->B, B->C) -> (A->C))

I don't understand how this evaluation goes to (() -> 0 1)

Did I evaluate up to the right step until then? And if so, how does the compose function pair up
numbers?
Reply

Replies

Jon Purdy February 15, 2012 at 8:55 PM


It’s easiest to do by substitution, without considering the types.

Start: 1 2 join2.
Expand join2: 1 2 quote swap quote swap compose.
Substitute quote: 1 [2] swap quote swap compose.
Substitute swap: [2] 1 quote swap compose.
Substitute quote: [2] [1] swap compose.
Substitute swap: [1] [2] compose.
Substitute compose: [1 2].

There is a bit of a notational problem here. The type T—one value—is equivalent to
the type () → T—function from 0 values to 1 value—which too is equivalent to ∀A. A
→ (A, T)—function from n to n+1 values. So while the type I gave for “quote” is
accurate, it is also somewhat misleading, which probably led you astray when you
were simplifying “compose”.

From these signatures:

() → 0 :: ∀A. A → (A, int)


() → 1 :: ∀B. B → (B, int)
compose :: ∀X Y Z. (X → Y, Y → Z) → (X → Z)
:
You should get these constraints:

1. X = (A)
2. Y = (A, int)
3. Y = (B)
4. Z = (B, int)

From which you can derive:

5. B = (A, int) [by #3 and #2]


6. Z = (A, int, int) [by #4 and #5]

So the monomorphic type of “compose” becomes:

compose :: (() → int, int → (int, int)) → (() → (int, int))

And thus the type of “(() → 1) (() → 2) compose” is “() → (int, int)”, as expected.

Dylan Mikus February 20, 2012 at 6:20 PM


Great, thanks for the help.

Reply

Tassilo Horn March 14, 2012 at 4:25 PM


Very nice post, Jon. It made me that curious that I've implemented my own concatenative
language, even before really grasping everything I probably had better known. But hey, it's
good enough to calculate the factorial! ;-)

It's here: https://github.com/tsdh/clj-miniconcat


Reply

JK April 21, 2012 at 12:24 PM


This is a great post! I was vaguely familiar with the formal notion of concatenative languages,
and this is an extremely clear explanation. Thank you.

Also, tangentially, this: http://home.iae.nl/users/mhx/sf.html


Leo Brodie's "Starting Forth" is IMO one of the best programming books ever - accessible to
smart kids (I read it the first time in 6th grade, I think), but with enough detail that you can
actually implement a Forth compiler after reading it.
Reply
:
Anonymous April 23, 2012 at 3:01 AM
I _think_ Dijkstra (the famous Edsger W.) used the term 'pointless' for what is here called
'concatenative', and 'pointed' for 'applicative'.
Reply

Replies

wtanksley April 25, 2012 at 12:14 AM


I'm familiar with the term "point-free", which refers to the original style of functional
programming developed by Backus. I'm not familiar with Dijkstra using a term
"pointless", and it wasn't a concatenative language Backus developed; it was
applicative.

Reply

Marnix Klooster April 30, 2012 at 4:26 AM


I found EWD 1116 (http://www.cs.utexas.edu/~EWD/ewd11xx/EWD1116.PDF), which uses the
term, in a similar setting.
Reply

Replies

wtanksley April 30, 2012 at 8:22 AM


Thanks. That does help -- it looks like "pointless" here doesn't mean the same as
"point-free" does. Here's an abstract that mentions and briefly defines the term:
http://www.mendeley.com/research/relation-algebras-and-their-application-in-
temporal-and-spatial-reasoning/

It looks like in this sense "pointless" means "without referring to the objects being
stored". There's a similarity, but it's a different area of discussion; and note that the
"pointless" proofs use plenty of names.

-Wm

Reply

It oklahoma May 8, 2012 at 1:32 AM


That's really a fantastic post ! added to my favorite blogs list.. I have been reading your blog
last couple of weeks and enjoy every bit. Thanks.
:
Reply

Anonymous May 8, 2012 at 3:20 PM


While one of the anonymous commenters above is right that most printers are not PostScript
printers, PostScript still builds the basis of PDF, so every PDF-Viewer is implicitly an interpreter
of a concatenative programming language:
http://en.wikipedia.org/wiki/PDF#Technical_foundations
Reply

Б.Б. May 28, 2012 at 6:57 PM


I find the article interesting and provocative. However, there are a number of things I disagree
with.

I don't like ‘concatenative programming […] uses function composition instead of function
application’ as a definition. Dropping one operation while promoting another does not mean the
latter is used ‘instead’ of the former; they mean very different things after all.

The statement ‘that function application becomes unnecessary […] makes these languages a
whole lot simpler to build, use, and reason about’ is not true in general – the author himself
gives an example of the opposite, later on in the article.

One interesting question is the relation between concatenativity, stacks, and postfix notation.
The author seems to maintain that stack is ‘not fundamental’ to concatenativity, but why then
all real, usable, concatenative languages are stack-based?

Also, if being postfix is also not fundamental and ‘there is nothing stopping a concatenative
language from having infix [or prefix] operators’, then why all concatenative languages are
postfix? Can concatenativity be preserved in the presence of non-postfix notation?

A commenter said that ‘semantics […] via row polymorphism […] makes the stack an
implementation detail’, but I see it exactly the opposite: the way it is used, row polymorphism
itself already seems to assume a stack organization (a row is a stack); by employing it to
define composition, the inherent relation of concatenative computation to stacks is only
accented.

In a comment following the article, the author says that ‘some of the distinctions that FP
systems maintain are arbitrary, such as between objects and functions’. In fact, that distinction
in FP is no more consequential than it is in concatenative languages. An FP programme
consists entirely of functions. Data objects only enter the picture when the programme is run.

Still further, he says: ‘row polymorphism makes the definition of a complete concatenative
system much cleaner and smaller; you can get away with only two stack combinators’. But of
course two combinators (e.g. S and K) suffice to express any computation in applicative style,
too – this sort of minimalism has nothing to do with row polymorphism or concatenativity.
:
The statement ‘the Forth language […] started it all’ is inaccurate. Forth is perhaps the most
influential in popularizing stack-based programming, but it was preceded by Pop (currently
known as Pop-11), which was stack-based and, unlike Forth, mostly functional.

The statement ‘because all we have are functions and composition, a concatenative program
is a single function’ is somewhat misleading, too – the said property holds of any (purely)
functional programme, not just concatenative ones.

The statement ‘most language VMs are essentially concatenative’ needs to be substantiated.
First, there is a well respectable set of VMs that are register-based, i.e., LLVM, Lua VM, Parrot,
Dalvik, and HASTE (Falcon's VM). Second, a VM may be stack-based but not concatenative.

The phrase ‘quotation […] allows deferred composition of functions’ is, I believe, incorrect.
Should it not rather be ‘deferred evaluation’?

Finally, the phrase ‘our mathematical language has evolved as inherently applicative’ needs to
be made more precise. That language is not only ‘applicative’. It is clearly variable-based, as
opposed to function-based (point-free). Functions very rarely are treated as values in
themselves. And, syntactically, infix notation is preferred wherever it applies.
Reply

Б.Б. May 29, 2012 at 5:52 AM


I find the article interesting and provocative. However, there are a number of things I disagree
with.

I don't like ‘concatenative programming […] uses function composition instead of function
application’ as a definition. Dropping one operation while promoting another does not mean the
latter is used ‘instead’ of the former; they do very different job after all.

The statement ‘that function application becomes unnecessary […] makes these languages a
whole lot simpler to build, use, and reason about’ is not true in general – the author himself
gives an example of the opposite, later on in the article.

One interesting question is the relation between concatenativity, stacks, and postfix notation.
The author seems to maintain that stack is ‘not fundamental’ to concatenativity, but why then
all real, usable, concatenative languages are stack-based?

Also, if being postfix is also not fundamental and ‘there is nothing stopping a concatenative
language from having infix [or prefix] operators’, then why all concatenative languages are
postfix? Can concatenativity be preserved in the presence of non-postfix notation?

A commenter said that ‘semantics […] via row polymorphism […] makes the stack an
implementation detail’, but I see it exactly the opposite: the way it is used, row polymorphism
itself already seems to assume a stack organization (a row is a stack); by employing it to
define composition, the inherent relation of concatenative computation to stacks is only
:
accented.

In a comment following the article, the author says that ‘some of the distinctions that FP
systems maintain are arbitrary, such as between objects and functions’. In fact, that distinction
in FP is no more consequential than it is in concatenative languages. An FP programme
consists entirely of functions; data objects only enter the picture when the programme is run.

Still further, he says: ‘row polymorphism makes the definition of a complete concatenative
system much cleaner and smaller; you can get away with only two stack combinators’. But of
course two combinators (e.g. S and K) suffice to express any computation in applicative style,
too – this sort of minimalism has nothing to do with row polymorphism or concatenativity.

The statement ‘the Forth language […] started it all’ is inaccurate. Forth is perhaps the most
influential in popularizing stack-based programming, but it was preceded by Pop (currently
known as Pop-11), which was stack-based and, unlike Forth, mostly functional. Pop-11 is still
finding use for AI education and research in UK.

The statement ‘because all we have are functions and composition, a concatenative program
is a single function’ is somewhat misleading, too – the said property holds of any (purely)
functional programme, not just concatenative ones.

The statement ‘most language VMs are essentially concatenative’ needs to be substantiated.
First, there is a respectable set of VMs that are register-based, e.g. LLVM, Lua VM, Parrot,
Dalvik, and HASTE (Falcon's VM). Second, a VM may be stack-based and not concatenative.

The phrase ‘quotation […] allows deferred composition of functions’ is, I believe, incorrect.
Should it not be ‘evaluation’ rather than ‘composition’?

Finally, the phrase ‘our mathematical language has evolved as inherently applicative’ needs to
be made more precise. That language is not only ‘applicative’. It is conspicuously variable-
based, as opposed to function-based (point-free). Functions very rarely are treated as values
in themselves. And, syntactically, infix notation is preferred wherever it applies.
Reply

Dobes November 15, 2012 at 1:26 PM


I've recently been learning Stratego/XT which is a program transformation language. Typically
terms go in on the left and are passed automatically from "strategy" (== function) to "strategy"
working left the right. Each strategy can transform the given term in some way to yield a new
one, or fail. Terms are composed by separating with a semicolon and you can take any
sebsequence of strategies and give it a name. So, it's another example of a concatenative
style of programming.

Some OO libraries that make use of this concatenative style, to good effect:

x.doThis().thenThat().andThisOtherThing().butWaitTheresMore()
:
It reads nicely compared to the functional reverse order:

andThisOtherThing(thenThat(doThis(x)))

You can factor out the middle parts into a new method, to some degree, like:

x.part2 = function() { return thenThat().andThisOtherThing(); }


x.doThis.part2().butWaitTheresMore();

I think probably the greatest hinderance to do concatenative programming style in static


languages like Java or C++ is that you cannot extend existing classes and defining all the
classes needed to represent intermediate states of the computation would become pretty
painful.

Reply

Unknown January 30, 2013 at 3:10 AM


Thanks for the article! It gave me a better understanding of how Factor works and which I am
attempting to master.
Reply

ComposureAndWill February 11, 2013 at 5:18 AM


I find that you did fantastic resolution when you selected this topic of the article here. Do you
usually create your articles alone or maybe you have a business partner or even an assistant?
Reply

Ross April 12, 2013 at 7:25 PM


This may be a really dumb question, but would it be reasonable to interpret a Combinatory
Categorial Grammar (http://en.wikipedia.org/wiki/Combinatory_categorial_grammar) as a
special purpose concatenative programming language?
Reply

Replies

Jon Purdy April 13, 2013 at 4:01 AM


I don’t know enough about the topic. At first glance, CCGs look more like APL in that
they’re tacit, infix, and based on application of terms to other terms. The concepts
are akin but not the same.

Reply
:
Anonymous April 14, 2013 at 5:09 AM
I wrote my own JIT/AOT compiler. In AOT mode you can do #exe{} and run JIT code during
compile time. You can insert code into the AOT stream. I did not count on lots of code this way.
Reply

Keerthi55 October 16, 2020 at 1:40 AM


nice code...
SSAS online training
SSIS online training
SSRS online training
Reply

Unknown December 4, 2020 at 4:07 AM


Thanks, this is generally helpful.
Still, I followed step-by-step your method in this Python Online Training
Python Online Course
Reply

meritstep December 9, 2020 at 10:02 AM


I just loved your article on the beginners guide to starting a blog.If somebody take this blog
article seriously
in their life, he/she can earn his living by doing blogging.Thank you for this article.
top java online training
Reply

KITS Technologies December 19, 2020 at 4:14 AM


Thanks again for the article post.Really thank you! Fantastic.
tableau training
Power bi online
Abinitio training
Reply

meritstep January 21, 2021 at 9:18 AM


I just loved your article on the beginners guide to starting a blog.If somebody take this blog
article seriously
:
in their life, he/she can earn his living by doing blogging.Thank you for this article.
best tibco sportfire online training
Reply

meritstep January 21, 2021 at 9:20 AM


I just loved your article on the beginners guide to starting a blog.If somebody take this blog
article seriously
in their life, he/she can earn his living by doing blogging.Thank you for this article.
best blueprism online training
Reply

meritstep February 3, 2021 at 9:32 AM

I just loved your article on the beginners guide to starting a blog.If somebody take this blog
article seriously
in their life, he/she can earn his living by doing blogging.Thank you for this article.
tibco sportfire online training
Reply

meritstep February 3, 2021 at 9:35 AM


I just loved your article on the beginners guide to starting a blog.If somebody take this blog
article seriously in their life, he/she can earn his living by doing blogging.Thank you for this
article.
blueprism online training
Reply

Vashikaran guru February 3, 2021 at 11:00 AM


After getting the mantras, you need to recite them daily for a few minutes. Within a few days,
you are going to get the desired results just by chanting mantras. Get Him Back After A Fight
Reply

Unknown March 22, 2021 at 2:05 AM


thanks for sharing valuable information...
Ai Course in coimbatore
Machine Learning Course in Coimbatore
placement training institutes in coimbatore
job oriented courses in coimbatore
:
mean stack training in coimbatore
php Training in coimbatore
digital marketing training in coimbatore
ethical hacking training in coimbatore
Final Year Project Center in Coimbatore
React js training in coimbatore
Reply

Anonymous April 8, 2021 at 2:11 AM


Thanks for sharing this in here. You are running a great blog, keep up this good work.
Online degree courses
Distance education
Reply

IamLinkfeeder April 8, 2021 at 3:40 AM


Cliff Saunders has spent over 20 years as a student of health and wellnessdisha patani boob
amala paul boobs nia sharma sex maitland ward nude babita porn deeksha seth nude kajal
nude pics tamanna bhatia fuck nude tamanna divya dutta nude
Reply

aswani April 13, 2021 at 9:26 AM


I am soo surprised to see this blog what an article keeps going thanks for sharing

Digital Marketing Training In Telugu


Digital Marketing Means In Telugu
Reply

PAIN KILLER PHARMACEUTICALS April 25, 2021 at 5:15 AM


where to buy gastrox oxide online at cheaper price
Buy Gastrox Oxide Online from painkillerpharmaceuticals.net

EMAIL; painkillerpharmaceuticalslink@gmail.com
Reply

Unknown April 28, 2021 at 3:01 AM


nice post..
revit training Institute in coimbatore
:
Cad layout design in coimbatore
Solidworks in coimbatore
Cad software training course in coimbatore
3dsmax course in coimbatore
Solidworks course in coimbatore
Cadd centre fee structure in coimbatore
Cadd course in coimbatore
Reply

Global Market Estimates May 19, 2021 at 7:15 AM


Non-alcoholic Steatohepatitis Treatment Market
Reply

Anisha May 20, 2021 at 11:07 AM


vehicle ownership transfer online delhi nice blog post
Reply

Anisha May 20, 2021 at 11:08 AM


vehicle ownership transfer online delhi nice blog post
Reply

meritstep Technology May 24, 2021 at 12:21 AM


Thanks for Sharing This Article.It is very so much valuable content. I hope these Commenting
lists will help to my website
microsoftazure online training
online training
workday hcm online training
Devops online training
Reply

Anisha May 24, 2021 at 7:12 AM


Mont Blanc Colognes | Mont blanc perfume |Mont blanc legend nice blog post
Reply

Anisha May 26, 2021 at 6:34 AM


:
Tom Ford Perfumes | Tom Ford Perfumes women | Tom Ford Fragrances nice blog post
Reply

Anisha May 26, 2021 at 12:31 PM


rc transfer in Ghaziabad nice blog post
Reply

Anisha May 28, 2021 at 6:40 AM


Best Jasmine Perfumes |White Jasmine Perfume | Jasmine Oil nice blog post

Reply

Anisha May 31, 2021 at 6:40 AM


Best Deodorants for Kids | Deodorant for kids | Best Deodorants nice blog post
Reply

women designer dresses May 31, 2021 at 1:17 PM


b2b research
great content
Reply

Anisha June 1, 2021 at 8:04 AM


Best Perfumes for Men| Best perfumes for men in the world|Best Fragrance nice blog post
Reply

women designer dresses June 1, 2021 at 12:34 PM


Russian transcriptions
awesome post
Reply

Unknown June 2, 2021 at 12:49 PM


Russian to English
awesome content
:
Reply

Anisha June 7, 2021 at 6:21 AM


Nonic toothbrush nice blog post
Nonic toothbrush https://nonicecoproduct.com/
Reply

Anisha June 11, 2021 at 6:35 AM


Best Montblanc perfumes for Women in 2021 – ( Reviews ) nice blog post

Reply

Anisha June 15, 2021 at 6:07 AM


Best Unscented Deodorants | Unscented deodorant | Natural Deododrant nice blog post
Reply

tejaswani blogs June 25, 2021 at 2:35 AM


"Wonderful contribution!" I'm blown away by the information you've shared. Thank you so
much for everything. Continue to write your blog.

digital marketing training in hyderabad


digital marketing course in ameerpet
digital marketing course training in hyderabad ameerpet
digital marketing online training in hyderabad
Reply

MÜSLÜM SEVDA June 27, 2021 at 11:22 PM


adana escort - adıyaman escort - afyon escort - aksaray escort - antalya escort - aydın escort -
balıkesir escort - batman escort - bitlis escort - burdur escort - bursa escort - diyarbakır escort -
edirne escort - erzurum escort - eskişehir escort - eskişehir escort - eskişehir escort - eskişehir
escort - gaziantep escort - gebze escort - giresun escort - hatay escort - ısparta escort -
karabük escort - kastamonu escort - kayseri escort - kilis escort - kocaeli escort - konya escort
- kütahya escort - malatya escort - manisa escort - maraş escort - mardin escort - mersin
escort - muğla escort - niğde escort - ordu escort - osmaniye escort - sakarya escort - samsun
escort - siirt escort - sincan escort - tekirdağ escort - tokat escort - uşak escort - van escort -
yalova escort - yozgat escort - urfa escort - zonguldak escort
Reply
:
Anisha July 6, 2021 at 5:15 AM
perfumes with patchouli nice and informative post thanks for the update
https://shop.fragrancereviews.in/ perfumes with patchouli
Reply

Unknown July 9, 2021 at 12:00 PM

Wow. That is so elegant and logical and clearly explained. Brilliantly goes through what could
be a complex process and makes it obvious.I want to refer about the best corporate Training
institute In Bangalore . They Provide 100% Placement Program .

Reply

Assignment Writing Help July 9, 2021 at 12:59 PM


We are a top rated marketing assignment help Online service here with experts specializing in
a wide range of disciplines ensuring you get the assignments that score maximum grades.
Reply

LK Sethy July 11, 2021 at 6:18 AM


SEO services in IndiaIn the Website Building segment, our strategy involves blending in design
and development with sustainable practice. In the end, your Business will be able to achieve
digital marketing goals with long-term growth. Top SEO companies in India

Digital Marketing Agency in IndiaWe are a Website Design Company and Digital marketing
agency worldly known for our super creative blend in the projects that we do deliver. Internet
Marketing Agency
Reply

Sarıhan July 13, 2021 at 1:55 AM


adanaescort01.com - adiyamanescortxx.com - afyonarackiralama.net - aksarayescort.net -
antalyaoyunpark.com - aydinescortkiz.com - balikesirescortlar.com - batmanescortlar.com -
bitlisescortlar.com - burdurescortlar.com - bursamalaysias.com - diyarbakirambar.com -
edirnedespor.com - erzurumyolkosusu.com - eskisehirescortlari.com - gaziantepekspres.org -
gebzeescortkiz.com - giresunmaraton.com - hataykoleji.com - ispartakpss.com -
karabukteknik.com - kastamonuajans.net - kayserivalisi.com - kilisescort.com -
kocaeliescortlar.com - konyaescortlar.com - kutahyaizemlak.com - malatyadataksi.com -
manisaescortlar.com - marasatasoyemlak.com - mardinfanatik.com - mersinmoda.com -
muglaapart.net - nigdeyapi.com - orduescortt.com - osmaniyeyorum.com - sakaryanur.com -
:
samsunescortlar.com - siirteyatirim.com - sincanoto.com - tekirdagescortlar.com -
tokatforum.com - usakbasin.com - vanescortilan.com - yalovadaemlak.com - yozgattanal.com -
sanliurfadayim.com - zonguldakescort.com
Reply

Unknown July 13, 2021 at 6:39 AM

Wow. That is so elegant and logical and clearly explained. Brilliantly goes through what could
be a complex process and makes it obvious.I want to refer about the best
sap abap training in bangalore . They Provide 100% Placement Program .

Reply

Unknown July 16, 2021 at 7:40 PM


cami avizesi - cami avizeleri - avize cami - no deposit bonus forex 2021 - takipçi satın al -
takipçi satın al - takipçi satın al - takipcialdim.com/tiktok-takipci-satin-al/ - instagram beğeni
satın al - instagram beğeni satın al - btcturk - tiktok izlenme satın al - sms onay - youtube
izlenme satın al - no deposit bonus forex 2021 - tiktok jeton hilesi - tiktok beğeni satın al -
binance - takipçi satın al - uc satın al - sms onay - sms onay - tiktok takipçi satın al - tiktok
beğeni satın al - twitter takipçi satın al - trend topic satın al - youtube abone satın al -
instagram beğeni satın al - tiktok beğeni satın al - twitter takipçi satın al - trend topic satın al -
youtube abone satın al - takipcialdim.com/instagram-begeni-satin-al/ - tiktok takipçi satın al -
tiktok beğeni satın al - twitter takipçi satın al - trend topic satın al - youtube abone satın al -
instagram beğeni satın al - perde modelleri - instagram takipçi satın al - takipçi satın al -
instagram takipçi satın al - betboo
Reply

Anonim July 18, 2021 at 3:14 PM


tutku iç giyim Türkiye'nin önde gelen iç giyim markalarından birisi olmasının yanı sıra en çok
satan markalardan birisidir. Ürünleri hem çok kalitelidir hem de pamuk kullanımı daha fazladır.

Digitürk sporun yıldızı Faturalıya ilk 4 ay 49,00 TL, daha sonra ayda yalnızca 154,00 TL. Kredi
Kartı ile 12 ay boyunca ayda 119 TL, toplamda 1428 TL.

nbb sütyen hem kaliteli hem de uygun fiyatlı sütyenler üretmektedir. Sütyene ek olarak sütyen
takımı ve jartiyer gibi ürünleri de mevcuttur. Özellikle Avrupa ve Orta Doğu'da çokça tercih
edilmektedir.

yeni inci sütyen kaliteyi ucuz olarak sizlere ulaştırmaktadır. Çok çeşitli sütyen varyantları
mevcuttur. iç giyime damga vuran markalardan biridir ve genellikle Avrupa'da ismi sıklıkla
:
duyulur.

iç giyim ürünlerine her zaman dikkat etmemiz gerekmektedir. Üretimde kullanılan malzemelerin
kullanım oranları, kumaşın esnekliği, çekmezlik testi gibi birçok unsuru aynı anda
değerlendirerek seçim yapmalıyız.

iç giyim bayanların erkeklere göre daha dikkatli oldukları bir alandır. Erkeklere göre daha özenli
ve daha seçici davranırlar. Biliyorlar ki iç giyimde kullandıkları şeyler kafalarındaki ve
ruhlarındaki özellikleri dışa vururlar.
Reply

Unknown July 20, 2021 at 6:41 AM

informative blog post. Thanks for sharing

Best Web designing company in Hyderabad

Best Web development company in Hyderabad

Best App development company in Hyderabad

Best Digital Marketing company in Hyderabad


Reply

Ekinciler Nakliyat July 21, 2021 at 4:39 PM


Profesyonel Bostancı evden eve nakliyat hizmetleri
Reply

Sam Daniel July 21, 2021 at 10:55 PM


Thanks for posting. Excellent information.
Power Bi Consulting Sydney
Power Bi Consulting Melbourne
Power Bi Corporate Training
Tableau Corporate Training
Power Bi Consulting Australia
Reply

vaibhav July 22, 2021 at 12:09 AM


Thanks for sharing this wonderful article with us. Your post is really good. If you are looking the
:
best assignment help service then kindly visit our website Cheapassignmenthelp.co.uk. For
more information kindly check below links:
Marketing Management Assignment Help Uk
HND Diploma & Advance Diploma in Construction Management
HND Warehousing Management
HND Diploma in Web Design
HND Diploma in Digital Marketing
HND Diploma in Project Management
HND Diploma in Entrepreneurship
HND Diploma In Pre U Foundation Studies
HND Diploma In Law
Case Study Assignment Help UK
Law Contract Assignment Help Uk
HND Diploma in Digital Marketing
New Graduate Application Assignment Help Uk
Case Study Assignment Help UK
Taxation Theory Law Assignment Help UK
Financial Analysis Coca COla Amatil Assignment Help Uk
Strategic Information Systems Business Assignment Help Uk
Market structure Absolut vodka Nepal Assignment Help Uk
Business Scenario Stitches Assignment Help Uk
New Graduate Application Assignment Help Uk
Reply

Unknown July 23, 2021 at 6:17 AM


instagram takipçi satın al
aşk kitapları
tiktok takipçi satın al
instagram beğeni satın al
youtube abone satın al
twitter takipçi satın al
tiktok beğeni satın al
tiktok izlenme satın al
twitter takipçi satın al
tiktok takipçi satın al
youtube abone satın al
tiktok beğeni satın al
instagram beğeni satın al
trend topic satın al
trend topic satın al
youtube abone satın al
instagram takipçi satın al
beğeni satın al
tiktok izlenme satın al
sms onay
youtube izlenme satın al
:
tiktok beğeni satın al
sms onay
sms onay
perde modelleri
instagram takipçi satın al
takipçi satın al
tiktok jeton hilesi
instagram takipçi satın al pubg uc satın al
sultanbet
marsbahis
betboo
betboo
betboo
Reply

jeslynwang July 25, 2021 at 6:24 AM


Nagaqq Yang Merupakan Agen Bandarq terbaik , Domino 99, Dan Bandar Poker Online
Terpercaya di asia hadir untuk anda semua dengan permainan permainan menarik dan bonus
menarik untuk anda semua

Bonus yang diberikan NagaQQ :


* Bonus rollingan 0.5%,setiap senin di bagikannya
* Bonus Refferal 10% + 10%,seumur hidup
* Bonus Jackpot, yang dapat anda dapatkan dengan mudah
* Minimal Depo 15.000
* Minimal WD 20.000
* Deposit via Pulsa TELKOMSEL
* 6 JENIS BANK ( BCA , BNI, BRI , MANDIRI , CIMB , DANAMON )

Memegang Gelar atau title sebagai AGEN POKER ONLINE Terbaik di masanya

11 Games Yang di Hadirkan NagaQQ :


* Poker Online
* BandarQ
* Domino99
* Bandar Poker
* Bandar66
* Sakong
* Capsa Susun
* AduQ
* Perang Bacarrat
* Perang Dadu
* BD QQ (New Game)

Info Lebih lanjut Kunjungi :


:
Website : NAGAQQ
Facebook : NagaQQ official
WHATSAPP : +855977509035
Line : Cs_nagaQQ
TELEGRAM :+855967014811
Reply

KEREM SEVDA July 26, 2021 at 2:22 PM


kayseri escort - hatay escort - kayseri escort
Reply

Melis July 27, 2021 at 7:10 AM


sms onay sitesi arayanlar bu alanda iyi olan sitelere bakabilirler.
Reply

Educational Services July 27, 2021 at 8:55 AM


Thanks For Sharing such a wonderful article the way you presented is really amazing
Best Software Training Institutes
Reply

mshahid July 28, 2021 at 8:07 AM


This is very interesting, You’re a very skilled blogger. I have joined your feed and look forward
to seeking more of your excellent post. Also, I have shared your website in my social networks!
Unique Dofollow Backlinks
Reply

Enter your comment...

Comment as: Google Account

Publish Preview
:
‹ Home

View web version

Powered by Blogger.
:

You might also like