I’m not the first one to complain about the weight of syntax rules nor am I the first one to claim Lisp syntax is simple. When trying to explain it to colleagues, it appeared brevity would be my friend.
I found a nice formulation. But it’s just a useful lie.
That’s all there is: chainable action perimeters. A bit of a mouthful but precise and clear. On top of that, it’s a notion that every programmer know and has already used.
In most programming languages, this notion is applied to function calls. It’s written like that:
myFunction(arg1, arg2, ...)
The perimeter is bounded by the parentheses and encompasses every argument. Each one is separated from others by a comma. And because it is first and foremost about an action, we have the name of this action in the front of the perimeter.
In Lisp, it is written as:
(my-function arg1 arg2 ...)
Here the name of the action is inside the perimeter. It makes sense too. We first and foremost write perimeters and when they are action perimeters, the first element must be an action name.
At this point, the place of the name is only a convention, just as it is a convention to use camel case (myFunction) or hyphenated names (my‑function). Or to use commas or spaces to separate arguments.
But what about the chainable property?
Well, a call to an action results in a value. You just have to believe it or set up a test to confirm that fact. Or, if you are lucky, use an interpreter. No need for belief or artificial test then.
Being a value, we can use it as an argument to some other actions. This is written as:
otherFunction( myFunction(arg1, arg2, ...), otherArg2, ...)
(other-function (my-function arg1 arg2 ...) other-arg2 ...)
Both notations are straightforward. We don’t have to resort to anything fancy to chain actions. We use the same notation, nothing more, nothing less.
You may be unimpressed. It’s mere conventions and the first one is better in the sense it’s far more used and thus more common than the second one, the Lisp notation.
I couldn’t agree more. This is all convention.
I believe you don’t see the beauty of both notations because of the very programming languages you are using. That was my case anyway. It took me a month to actually see what I had in front of me. It took me a month to understand I can just chain.
Indeed, it rarely is idiomatic to chain in most programming languages. And since this notation is restricted to function calls, you can’t chain much.
Want to write a condition? This is not the syntax you need. Want to write a loop? This is not the syntax you are looking for (yes, Jedi-trick). Want to define a function? Move on, nothing to see.
All these are answers that most programming languages give us. On what ground? Conventional ones it appears. Lisp dialects differ in their answers. They say we can continue in the same way and not that we must learn something else before going on. There’s nothing foolish in chaining.
Because it just works.
Indeed: a condition? Meaning select what to do based on a condition? That sounds like an action. Well, we already know the syntax. A loop? That is repeating a series of actions? Action too, we know how to write it down. Define a function? Hell, the verb is just there right under our nose.
It appears to me that most programming languages jedi-trick us into thinking only a tiny bit of the things we ask the computer to do are actions. We have so indulged into spending most of our time in the universe of text that we ended up talking to compilers rather than interpreters.
So the focus is on syntax and text structure rather than semantics and results. Actions are then a pure notation trick to encapsulate things. The compiler only understands the text apparatus, not the notion itself. Thus it doesn’t make them a reality. Actions are not values.
Let’s forget for a moment the specifics of talking to a compiler. When we ask the computer to do something, that’s an action! And the job of any programming language is to name such an action or provide a way to let us define its name.
And on that last point, there’s an action again. We want to define actions. Where is the need for special syntax? Why have actions as syntax artifacts rather than values?
The language designers may have been very thoughtful, syntax can be like violence. It can form a descending spiral by its very nature alone. It calls for even more special rules since it’s not like our keyboards have a wealth of symbols.
At one point, you may deal much more with text rather than code and actually the line gets blurry. For instance, I am prone to only understand the text apparatus and confuse it for the notion itself. And it’s not the rarity and brevity of traveling to the universe of results that will help me disentangle my thoughts.
Escaping this text violence is nice but how can one rule ever appear to be serious? Even a child could learn it! Surely it can’t be working for a serious general programming language™.
Yet after one month of programming in Common Lisp, I reached the famous illumination that chainable action perimeters accurately and straightforwardly express every action I want the computer to do.
In fact, I had to looked back to be sure of it. The answer was a definite and concrete yes. I could see all my code with conditionals, loops, function definitions, variable definitions, class definitions and even more, written this way.
This only way.
But Common Lisp does not encompass every programming practice, far from it. I think it has elegantly nailed the most basic and naive way we can program. Which is… imperative programming. I’m not sure how to put it less bluntly but well, this is it.
It’s just that when a language does not expose you to many syntax rules, helps to connect results of actions to other actions, consider actions as values and code as data, you can easily express the computations you seek.
You do not need to fill your head with many syntax rules and when they are valid or not. You do not need to stop every once in a while and do some variables plumbing to connect results to other actions. You need far less design patterns. And you can ask the computer to generate code for you, based on code you write in the very same language you want it to be generated . Or not.
The important point is that… okay, okay, chainable action perimeters.
It is this: about fifty years have passed since the birth of the first programming languages aiming at generality. Countless ones have now disappeared and will clearly not make any distinguished contributions in any professional setting. And many others still get invented.
While the majority of these last ones have been getting their inspiration from several programming paradigms, most of the time their gist is imperative programming. Even in object‑oriented or functional programming, chaining actions is a fundamental notion. Whether you ask an object to perform something or apply a function to some data, the bottom line is that you want the results and use them somewhere else.
The point was that I only needed to look at it for 5 minutes and I was good to go. It took me a month because my programming experiences were telling me things cannot be that simple.
Yet they can. I didn’t need to stare at Common Lisp right in the eyes and grok a pile of syntax rules, find some more libraries, pull the world in my workspace, consider yet another framework, discover tools and libraries mismatching, then trying to mend, plumb and adapt and finally know way way more about the underlying inner workings right from the start.
I think the ultimate Jedi‑trick is shielding the programmer from the inner workings by shying away from making things values. Or results if you prefer. I find it distressing when the language helps me to produce something that it is powerless to help me understand, let alone master. Because it is helpless at making me exploring it.
Programming is full of trade‑offs and whatever the language —for me, it just happened to be Common Lisp— I really became better at it because the inner workings were clear, at least at a high level. Then because they were explorable. That implied it was possible to move on to more interesting problems.
Frankly, this is all I’m looking for.