Faoileag's Nest

My Home On The Net

Home Projects Soapbox Writing About Imprint Data Privacy Statement

Some Words on Common Lisp

Code in Common Lisp may look strange but isn't.

It may look strange at first, but once you understand the concept(s) of the language, everything falls into place and the code turns from hardly readable into an easy-to-read, straightforward collection of instructions.
Like those pictures where you have to re-focus your eyes to see anything other than noise.

In Lisp, everything is a function

In Lisp everything, yes absolutely everything, is a function (or a macro, but I won't go there in this brief introduction).

Output is a function (ok, nothing new here). Comparison is a function. Conditional execution is a function. Addition and assignment are functions. Everything is a function.

The "everything" bit is the hardest part here, together with semantics. In C++ operator overloading is handled in Lisp style, with the operator being represented as a function with two arguments, so yes, for the someone transitioning from C++, the concept should not be an altogether new one. But not everyone comes across operator overloading in their career, and even if they do, the C++ semantics hide the fact that operators are functions, whereas in Lisp they don't (that's infix vs. polish notation for you).

Lisp uses Polish Notation

And Polish Notation means: the first thing you write down is the operator, then you supply the arguments. Like "+ 1 2" instead of "1 + 2".
Lisp is the only higher-level programming language I know off that uses that concept (assembly languages do as well, but that's not due to specific language design but rather a side effect of assembly being thinly disguised machine language code). All other higher-level languages use infix notation: "1 + 2".

The only other language I know of that doesn't use infix is the programming language of the HP 48x pocket calculator, which, of all things, uses Reverse Polish Notation, where the arguments come first and the operator comes last: "1 2 +".

Polish Notation is something you have to get used to but once you accept that operators like "+" are functions it is not so strange anymore: in Lisp, like in other higher-level languages, the semantics of writing a function call are "<function-name> [arg1] ... [argn]". So there's nothing fundamentally new here.

In Lisp, function calls themselves start with a "("

And not, as in other languages, the argument list of the function call.

Again, that makes perfect sense once you see the function's name as the first argument to some generic function-invoking function that takes the name of the function to invoke as its first argument, followed by the arguments to invoke that function with.

So "(" would be the function-invoking function, "+" would be the name of the function to invoke, and "1" and "2" would be the arguments to the invoked function.

This is not entirely uncommon. In the unix world, when you parse command line arguments, the first argument is always the name of the program called. Which is precisely how the above mentioned function-invoker would see the world.

All those closing brackets make things unreadable!

Well, all things must come to an end at some time. In C++, the beginning and end of a function are marked with curly brackets, in Python, the function's body is marked by the means of indentation and in Lisp the end of a function's body is marked with a ")" to match the opening quote (or hypothetical function-invoker, see above) "(".

So no fundamental difference to C++ or Java here. The difference you observe comes from the different coding style you employ in Lisp: all functions closing brackets are put into the same line.

In C++ the opposite is true: for closing brackets the commonly accepted style is one line for each bracket, and that irrespective of you following Kernighan & Ritchie style or Allman (at least so far I haven't seen company coding rules that did not mandate one line per bracket).

But of course you could do it Lisp style as well:

if ( predA ) {
  if ( predB ) {
    if ( predC ) {
      cout << "All predicates fulfilled!"; }}}

Doesn't look that much different from Lisp now, does it?

The problem is a bit intensified in Lisp though, because in functional programming you regularly do something you rarely do in imperative / procedural style (or in OOP for that matter): function chaining, where you pass a function call into the argument list of another function.

Example: (inc (inc (inc 1))) will deliver 3 assuming that the "inc" function increments its argument by 1.
And function chaining is usally written in an inline style, and that means a lot of closing brackets.

"(* 2 (+ 4 (inc 1)))" (which in turn should return 12). Taken to extremes (e.g. with nested if-calls), this can indeed lead to very long groups of closing brackets.
But special as this may sound, you do have the same problem in other languages, too. In C++, nested blocks would be closed in much the same way, only using curly brackets ("}}}}") instead of normal ones ("))))"). Only, in C++, this is frowned upon and coding styles usually mandate only one closing bracket per line. Which in turn is a practice that you can follow in Lisp as well, if it makes the code more readable for you.