Sam Gentle.com

Being vs doing

I've been thinking about the nature of programming, specifically the abstractions that you build everything else out of. I've argued before that computers are special because they represent an abstract unit of action. But the formalisms of computer science often quickly turn into mathematics, and nowhere is this more true than in functional programming.

In functional programming, the core primitive is the function. Church's lambda calculus is capable of expressing any computation in terms of functions, and is perfectly viable as a model of computation. However, a function isn't a unit of action. It doesn't do something, it describes things and the mappings between them. Functional programming takes computation and turns it into another kind of mathematics; nouns rather than verbs, definitions rather than actions.

You can see how far from a unit of action the function is by looking at how functional programming approaches sequencing. How do you say "do this 10 times"? You simply define your 10th step in terms of your 9th step, your 9th step in terms of your 8th step and so on. Thus, to turn your question into your answer the 10 steps must be evaluated in order. Anything essentially non-definitional is handled by widening your definitions, up to and including defining the world outside of your program as part of your program.

That's not to say these functional abstractions don't work, or can't be extremely elegant when your problem is definitional in nature. However, what if your problem is not definitional? What if it is essentially actional, and best modelled in terms of steps or sequences? In that case, these abstractions become nothing better than glue code designed to pave over the impedence mismatch between your problem and the tools you're using to solve it. It's easy to mock Java programmers for working around every deficiency in their language with more patterns and more classes, but why is doing the same with more functions any better?

The Church–Turing thesis tells us that imperative and functional programming are equivalently expressive. And yet when we turn to theory, functional programming is seen as more rigorous, more serious, somehow higher and better than imperative programming. Why? I believe it's just more compatible with existing mathematics. It's easier to define things about computation if your computation is also made of definitions. It's not the only way, though, and more actional formalisms exist, like process calculus, state machines, and the original Turing machines.

It's interesting to ponder about what the equivalent of a modern high-level functional programming language built around actions would look like. Of course, many popular programming languages are imperative, but even so one of the first things introduced to imperative programming languages is functions, and with them recursion, statelessness, and other functional concepts. Even without functional programming, it's easy to end up in a kingdom of nouns. What would it look like to be fundamentally verby?

I think one of the key hints that you have found such a language is that, much as you need extra layers of abstraction to express actions in functional programming, a truly actional language would take extra steps to express functions. It is telling, perhaps, that in Forth and other concatenative languages, the most difficult thing to express is mathematics. Could it be that concatenative programming is the path to a language of action?