1. Why I like Lisp.
  2. What I dislike about Lisp.

Why I like Lisp

Introduction

I've recently started attempting to learn the Lisp programming language. My previous attempts always aborted at the wierd (to me) syntax, and the wierd (again, to me) programming style.

But, I have to say now that I've finally got over my parenthical prejudices, I'm bloody well impressed! The power of lisp is difficult to explain to someone who does not actually program with the language, but I'm going to try and give a single simplistic example to try and illustrate the power of lisp.


The language versus the library

First off, even though I do not particularly like arguments that go "Yeah, but can your language do this?", I am nevertheless going to ask "Can your language do this?".

The this in question is the following:

Assume that your favourite (and by implication, your most powerfull) language does not support a certain bit of functionality ... say the ubiquitous for loop, although YFL (Your Favourite Language) does indeed have a while loop.

So, how hard is it to add a for loop to YFL? Can you do it without the sources to rebuild the language? Can you do it in a manner that makes it transparent to the language user?

In lisp, it is trivially easy to add into the language new constructs that expand the language. If there is no for loop, then you can add one in, and it will look, to the reader, as if it is part of the language itself.

Now, I'm not talking about adding a new function to the language, which can be done in every language you'd care to name, but adding a new language construct.

That's a big win, right there. Theres no need to even go further - in most other languages you would need to modify the source code for the language implementation whilst in lisp you can merely use the language as given and add in your more powerful constructs.

Example: Adding a language feature to lisp

For a concrete example, lets talk about reflection. You know, the ability for a class or complex piece of data to make available at runtime all the elements that make up said class or piece of complex data[1].

Lets say, that in YFL, you can declare something along the following lines in my easy-to-understand pseudocode:

   class Human inherits from Mammal {
      Name;
      Age;
      DoB;
   };
You can easily come up with the analogous definition in C#, C++, Java or any of the other popular languages. In lisp, such code can be expressed by:
   (defclass Human (Mammal)
     ((Name)
      (Age)
      (DoB)))
Of course, there are other things as well, such as setting default values for each element which is done as follows:
   (defclass Human (Mammal)
     ((Name :initform "Default Name")
      (Age  :initform 25)
      (DoB  :initform "1-Aug-1971")))
In addition, to ":initform", there are a few other keywords that one can place next to the element, such as ":initarg" and ":accessor", and they are placed in much the same way as ":initform" above. The value of an element is typically retrieved with "(slot-value ...)":
   (defvar foo (make-instance 'Human))
   (slot-value foo 'Name)
The above will presumably return the value that is stored in the element (called slots) "Name" in object "foo".

Now take YFL, and try to change it so that every class that you define a certain way automatically has one additional element - an element to store the names of all the other elements.

What I mean is this, after I instantiate a "Human" class, I would like my code to be able to get the name (in whatever form) of every single element in that class; example:

   Human  foo;
   print ("the names of all the elements in foo is");
   print (getAllTheNames (foo));
   if (foo.HasElement ("Name")) then
      print ("Name is an element of foo");
   end;
I'll even make it easy for you, I'll let you define a base class which all the other classes have to inherit from in order to have this functionality.

I suppose you can do it; in C or C++, for example, you can mess with the preprocessor until you get it working. Possible.

Totally impossible in Java without making the syntax of the call very ugly (I know, RMI needs reflection, but its ugly and cluttered with keywords and sequence of function calls to make it work). The pseudocode above has no equivalent in most other languages without writing bags and bags of supporting code.

In lisp, however, the following macro does exactly that:

(defmacro def-rclass (name base slotlist)
  `(defclass ,name ,base
     ((slots 
       :accessor slots
       :initform (let ((slotnames (list )))
                   (dolist (slot ',slotlist)
                     (setf slotnames (append slotnames (list (car slot)))))
                   (dolist (b ',base)
                     (setf slotnames (append slotnames (slots (make-instance b)))))
                   slotnames)
       ) ,@slotlist)))
And that, I kid you not, is not the best implementation of reflection in lisp (I am after all only just learning the language, an expert will almost surely do better, especially in the appending to a list bits). The usage is almost identical to the standard usage:
   (def-rclass Human (Mammal)
     ((Name)
      (Age)
      (DoB)))
Everything but the defclass keyword stays the same. Inheritance is specified the same as before, and multiple-inheritance works exactly the same as well (since we are not working with a base-class at all in the new definition, MI doesn't get affected at all). The keywords ":initform" and friends also work unchanged.

The list of element names (like "Name" "Age" and "DoB") still exists and can be accessed as before, but there is one new element that is created and modified invisibly; slots.

slots is an extra element that will be present in every single object instantiated from a class defined with our new def-rclass keyword. The contents of this element is simply a list all the other elements.

This:

   (def-rclass Human (Mammal)
     ((Name)
      (Age)
      (DoB)))
gets turned into this:
   (defclass Human (Mammal)
     ((slots :initform '(Name Age DoB))
      (Name)
      (Age)
      (DoB)))
by our macro def-rclass. But thats not all, you see def-rclass does not mess with the standard element values, so this:
   (def-rclass Human (Mammal)
     ((Name :initform "NoNameBrand" :accessor m-name :initarg :name)
      (Age)
      (DoB)))
gets turned into this:
   (defclass Human (Mammal)
     ((slots :initform '(Name Age DoB))
      (Name :initform "NoNameBrand" :accessor m-name :initarg :name)
      (Age)
      (DoB)))
so that the basic class definition is intact as if one had written it natively using plain old defclass. We can even get the inheritance and multiple-inheritance (with the order) correct, because this:
   (def-rclass Human (Endoskeleton Primate Hairy)
     ((Name)
      (Age)
      (DoB)))
gets turned into this:
   (defclass Human (Endoskeleton Primate)
     ((slots :initform '(Name Age DoB))
      (Name)
      (Age)
      (DoB)))
Impressive, huh? But wait, theres even more! Lets say you have more than one class, and that one inherits from the other like so:
   (defclass Endoskeleton ()
    ((BoneType)
     (BoneCount)))

   (defclass Primate ()
    ((Weight)
     (Height)))

   (defclass Human (Endoskeleton Primate)
    ((Name)
     (Age)
     (DoB)))
Then, instead of using defclass you use def-rclass to define all three classes. The final class Human will have the element slots set to '(Name Age DoB BoneType BoneCount Weight Height)). That is, the following:
   (def-rclass Endoskeleton ()
    ((BoneType)
     (BoneCount)))

   (def-rclass Primate ()
    ((Weight)
     (Height)))

   (def-rclass Human (Endoskeleton Primate)
    ((Name)
     (Age)
     (DoB)))
will get turned into this:
   (defclass Endoskeleton ()
    ((slots :initform '(BoneType BoneCount))
     (BoneType)
     (BoneCount)))

   (defclass Primate ()
    ((slots :initform '(Weight Height))
     (Weight)
     (Height)))

   (defclass Human (Endoskeleton Primate)
    ((slots :initform '(Name Age DoB BoneType BoneCount Weight Height))
     (Name)
     (Age)
     (DoB)))
so that you can have access to the name of every element, whether or not it's been inherited or defined locally in that class. I'm struggling to see how to implement this in other languages.

I implemented this while still learning about CLOS[2]; it simply occurred to me to do this in order to save all my objects to files and then read them back in a platform independent manner. I saved the objects simply as "name=value" pairs, where the "name" was the slotname and the "value" was the value of that slot.

This is not to show how to do reflection in lisp, but to illustrate the possibilities that the programmer has to solve his problem. There are many other powerful aspects of lisp such as the simple syntax (which will take all of 60 minutes to pick up and remains consistent throughout the language) or the ability to have anonymous functions (which are getting popular in other languages now) or even the full-featured error handling system (which makes exception throwing and catching in other languages look like a toy system) but this example shows best, I think, the power of having a programmable programming language.


Notes:
[1]

The python readers don't have to sit this one out; pretend that the python dir function did not exist, and you are trying to add it in, like the way I asked how hard would be to add for to the language had it not existed. The point is not to show how reflection and introspection can be added to a class, but to show that it can, in fact, be done with ease without any primitive reflection or introspection functions available in Lisp.

[2]

The Common Lisp Object System, possibly the most powerful object system I've had the pleasure of using.