What „Lispy“ means to me

Well, its quite a long time ago since I had my last real Meta-Lisp-Post, so well, why not having it now, while waiting to get tired to go to bed.

So well, „Lispyness“ is something which is sometimes discussed when talking about solutions for problems. „Lispyness“ is the reason why it is so hard to create widely-accepted and widely-used ffi-bindings, „Lispyness“ is the reason why some software is less efficient than it could be made, and in general, „Lispyness“ can be the reason for quite a lot design decisions. But even though there is a common sense about some aspects of  „Lispyness“, the concept „Lispy“ is not clearly defined.

I am now giving a few things which are essential for a programming language (or concept) to be „Lispy“. They are just my opinions, and they are likely to change in the future – at least partially.

Simple – one thing which is essential. Having a small core which can be easily implemented. And removing boundaries rather than adding new features. An example for such a lisp-feature is the reader-macro technology of common lisp (even though common lisp doesnt really have a small core) – you dont have to change the standard of the language to add new syntax structures to it. The syntax in general – bare S-Expressions – are such a thing. Macros in general are such a feature. In fact, this was one aspect which convinced me of Java (at a time when I didnt know Lisp). Java had about 50 keywords, a simple Reflection API, and a huge library – but these keywords were well-defined, and you could basically do everything whith them, while the library was just a bunch of classes which is a nice thing to have, but is not part of the core of Java. With a simple parser, one could produce an own Java-Interpreter and Compiler – unfortunately, the Java Bytecode is a lot less simple, and most of the Library was already compiled.

Liberal – remove boundaries if there is no need for them. Something I often notice in computer science in general is that boundaries are made to systems, even though they do not really make any sense. They are just there, because nobody cares, but sometimes they can produce problems. For example, it really took a long while until finally someone added DrawingCanvases to webpages – for a rendering engine, such an object is not hard to realize. Same for videos and sound. But instead of just providing it, and maybe extending its API, a whole plugin technology evolved, trying to substitute this lack. Most lisps are liberal in what you can do. Macros let you generate code, but in theory, you can download that code while compiling – which is not nice and shouldnt be done, but can be useful in some certain situations, in which nobody has to find a hack around that problem. The point is to understand the difference between the things one can do and the things one should do.

Dynamic – also essential. You dont build ferroconcrete blocks, you build lumps of mud. You use dynamic structures like lists, trees and structured objects, rather than having some static structure which is hardly bound. Comparing the Apache Web Server with the Hunchentoot Web Server is maybe the best way of expressing this difference: Apache is a static webserver – it has a plugin technology, so it can be extended, at least if you know how to adapt the configuration files and you restart it everytime you change them. Under hunchentoot, you can have a REPL running inside the Lisp-Process running your webserver. You can add and modify sites on the fly, try new settings without restarting the server, and change its behavior while running. Outside of Lisp, this can be found inside JavaScript, for example – objects can simply be extended by new slots, almost nothing is static. I think this is something all the Lisp-Dialects share somehow. Another example is the CLOS – actually, what you do by extending generic functions is building dynamic case-decisions.

Imperative – arguable, but my opinion. I dont understand why Clojure and Scheme try to get rid of the imperative features they have. Imperative languages became discredited in the scientific world, maybe because many imperative languages lack of modern features. Anyway, to me Lisp shows how imperative programming can be done well. In many situations, a functional approach is the most natural approach of doing something. But sometimes, when doing tail-recursive loops, etc., it just gets artificial. Sometimes an imperative algorithm is just more natural than trying to put it into a tail-recursive form. And also, sometimes, prog-go-forms („goto“ – for the C-Programmer) is simply the easiest way of programming something. To quote one of my professors: Programming Languages are not made for the computer, they are made for the humans. And they are also not made for some strict Type-System, they are made to give an easy and convenient way of expressing algorithms. Standard ML also has imperative features. And even Haskell has them – encapsulated inside monads. Monads may solve formal problems, but in the end, to me they are as artificial as forbidding the usage of „goto“-commands – trying not to use something which can cause problems when not used correctly. Lispyness means Imperativeness to me. I think I wouldnt consider a language which is not imperative in some sense as a lisp.

Chaotic – also arguable, but to me also essential. If it is completed, it is not lisp. There is always something that can be made better. There is always some edge which needs an additional hack. There is always a library which doesnt run on some platform. Thats software – but most software tries to hide it. Its – to me – the spirit of lisp to just accept it, and try to get along with it as good as possible. Well, there is a huge standard for Common Lisp – but outside this official standard, few inofficial standards really evolved, and the different implementations of Common Lisp are really behaving different. This can get on ones nerves, but you can always find a simple workaround – and minimize the need of changes to the code you already wrote. When looking at C/C++, you basically have one possibility – namely preprocessing instructions – to influence the compiled code, and adapt it to the compiler you recently use – but often you have to write the same thing twice, and as far as I see, more often than under Common Lisp. For Scheme, there is only a small standard anyway, and the compilers differ – as far as I see – in almost anything besides this standard (and even inside it, sometimes). Portability is very important to me – but it can only be gained when anybody tries to make his own software as portable as possible, as there are situations in which portability is not possible or not even useful. When somebody writes a binding for some elementary Linux-Syscall to make file-access more efficient, of course this can not necessarily be used under Windows, and so, anybody using this binding will have to write a wrapper around it, using normal file-access (or something similar for windows), to run it under windows, but as long as everybody keeps trying to write his code as portable as possible, and in a way such that it can be easily ported if somebody needs it, portability of the software in the end will be only a small issue.

So. Some aspects of lispyness, or what it means to me.


Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:


Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden /  Ändern )


Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )


Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )


Verbinde mit %s

%d Bloggern gefällt das: