
A concatenative programming library in 5 lines of Clojure - cpleppert
http://blog.fogus.me/2013/01/06/pesto5-a-concatenative-programming-library-in-5-lines-of-clojure/
======
tikhonj
Amusingly, just like whoever wrote the Haskell example linked in the blog, the
very first thing I did with free monads was also write a trivial stack-based
language. All I supported was pushing numbers to the stack and addition. Not a
very complicated language! Programs look something like:

    
    
        prog = do {1; 2; add; 3; 4; add; add}
    

The cool thing is that I get "subroutines" (or, as Forth calls them, "words")
for free:

    
    
        incr = do {1; add}
        prog = do {1; incr; incr; incr}
    

The interesting thing is that incr and prog are actually ASTs: I can run them,
with a function called runProgram, but I can just as easily pretty print them.
This is a wonderful property of free monads that's very useful for _real_
DSLs. And, in fact, I'm using them for a current project--they're great.

With a bit of cleverness, it's probably possible to encode some of the stack
semantics into the type system, making running a program with too few or too
many elements left on the stack a type error. Also, with GADTs, I can probably
add support for something like strings and have a mini string/int type system
just for my little language, without much effort.

~~~
thejsjunky
> it's probably possible to encode some of the stack semantics into the type
> system, making running a program with too few or too many elements left on
> the stack a type error

Indeed, Factor does this. In addition to checking for stack (over/under)-flows
it's what allows really powerful behavior like the ability to say "run both
these words with the current stack" without having to explicitly state how
much of the stack to duplicate.

------
evincarofautumn
This isn’t concatenative except in the trivial sense of “stack-based”.
Concatenative languages are functional languages with compositional semantics,
which often happen to be most efficiently implemented as stack machines.
Concatenative languages are interesting because they’re a combinator calculus
like SKI[1] but with simpler semantics and useful algebraic properties.

Only allowing two-argument functions doesn’t cut it, and you need some means
of returning multiple values as well. You also need a means of abstraction
(e.g., Clojure fns) and combinators for application, abstraction, and stack
manipulation. A full implementation of an interpreter for a concatenative DSL
would be rather longer than this—though still probably in the realm of 100
lines.

[1]: <http://en.wikipedia.org/wiki/SKI_combinator_calculus>

~~~
fogus
I'm not sure that the presense of a stack is required for a concatenative
language, but your point is well taken. Pesto5 is not enterprise ready.

~~~
evincarofautumn
Right, that’s what I said. Just because it’s efficient to implement one
language that way doesn’t mean it’s efficient to do that for _every_ language
in the same family. :)

With non-variadic arithmetic functions:

    
    
        (defn add [x]
          (cons (+ (first x) (second x)) (rest (rest x))))
    

Your “postfix” function can remain a simple “reduce”, but can be totally
agnostic of function arity. That frees you up to write the basic combinators:

    
    
        (defn dup [x]
          (cons (first x) x))
    
        (defn zap [x]
          (rest x))
    
        (defn swap [x]
          (cons (second x) (cons (first x) (rest (rest x)))))
    
        (defn ap [x]
          (apply (first x) (rest x)))
    
        ...

------
thejsjunky
Stack based and concatenative programming languages are something I find
really interesting. It's really easy to implement a simple one, so one of my
go-to benchmarks for trying out a new language is to implement a little stack
based language in it.

They're a lot of fun, and some simple ideas can lead to really interesting
results. Since all code is just a sequence of tokens it becomes trivial to
treat words, quotations, and entire programs as lists and manipulate them a la
Lisp. Another nice thing is many such languages give you access to the parser.
One I wrote recently is (almost) interpreted directly from text and let's you
hook into the parsing process. It doesn't have comments, but it's trivial to
add them:

    
    
        IMMEDIATE: /* BEGIN NEXT-TOKEN STRING */ EQ? UNTIL ;
    

This turns "/*" into a function that will drop tokens until it comes to a
terminating marker. Such functionality can be used for macros or to add to
DSLs.

Also interesting is how the stack changes the way you write code. What you end
up with is program that's entirely made of something like pipes in ML-like
languages or chaining constructs in JavaScript. They're very easy to compose
together (and generally there's less overhead) so it encourages "factoring"
out into lots of little pieces; something you can do in almost any language
but doesn't feel as natural IMO.

------
dherz
You might have a look at Consize, a concatenative language I implemented in
Clojure in 140 LOC. Consize is very much inspired by Factor, however Consize
is a functional language like Joy or Cat is.

<http://code.google.com/p/consize/>

It is extensively documented in German — I plan to add some documentation in
English as well. But you might enjoy reading the source code anyhow ;-) After
reading consize.clj, I recommend to continue with prelude-plain.txt.

BTW: Adding object-orientation (i.e. polymorphism via generic words & multiple
inheritance much like Clojure does) is possible in about 30 LOC.

Dominikus

