Wednesday, March 09, 2005

 

Fun With Lisp

I have occasionally played with Lisp and Scheme in the past, but never long enough to get a good feel for the language. Lately, however, I have spent a significant amount of time working with Lisp and I am really beginning to appreciate the beauty of the language.

I have started a web page for Lisp stuff where I will put some of the interesting things I come up with. So far, it has an implementation of the Fibonacci Heap, which I can tell you is a bear to implement. I also have an implementation of Dijkstra's Shortest Path algorithm and some code to parse Tiger/Line files.

Why do I think Lisp is cool?

First of all, the syntax is extremely simple. Yes, there are a lot of parentheses, but I think the reason that they stand out so much is that, apart from quotes, there's not a lot of punctuation required. You don't have to end statements with a semi-colon, or put commas between list items, or put curly braces around statements. It is simple and direct.

First class functions are really cool, as well. Although they are not unique to Lisp, first class functions are not found in Java, which is my current day-job-language. By saying a function is "first class", it means that a function has the same status as other data items. You can pass a function as a parameter to another function. You can store functions in other data structures. You could, for example, iterate over a list and apply each function in the list to a particular piece of data. This would make it easier to apply business rules, for example. You can do this in Java with Interfaces, it's just a bit easier in Lisp.

There are lots of functions for dealing with data structures. For all the different containers that Java has, it really falls down on the job when it comes to actually using them. One of the things that always impressed me about Smalltalk was the things you could do with containers (Ruby has this as well, and Python's list comprehensions are close enough). In Lisp, for example, there is the (map) function, which in its simple form is just like iterating through a list with a for loop.
For example:

(mapc #'print '(1 2 3 4 5 6 7))
loops through a list and calls the (print) function for each item in the list. There are numerous (map) functions, some return no result, some return a list of the result of applying the function. Where the (map) functions excel beyond a simple loop, however, is that it can use multiple lists and invoke functions that take more than one argument. A simple example would be adding together corresponding elements in two lists:

(mapcar #'+ '(1 2 3 4 5) '(6 7 8 9 10))
which returns the list
(7 9 11 13 15).

There are other cool things, one last one I'd like to mention is that a Lisp environment tends to be highly interactive. You can write some functions and immediately interact with them from a Lisp prompt.

Comments:
The Lisp implementation I am using is SBCL. It can compile to native code. My IDE is Emacs, with a package called SLIME (Superior Lisp Interaction Mode for Emacs).

Slime provides some handy features, one of which is that it can show expected function parameters on the status bar. It's like the drop-down menus you get in Java/.Net IDEs, but less intrusive. It also has completion with c-c TAB. Being a vi user for 21 years, it has been difficult retraining myself to use EMACS, but it is starting to grow on me.
 
Thanks for mentioning Nice, Toby. Mike mentioned it at lunch the other day as well. I didn't realize that it appears to have first-class functions.

Looking at some of the code on the web site, I have to say that my first reaction was "Gaaah! It's C++ returned from the grave!" Then I calmed down. Nice seems to improve on some of the shortcomings of Java. I've been reading a bit on Haskell and OCaml as well, and they also show that you can still get a lot done in a statically-typed language.

The interactivity is really more than whether there is an interpreter, though. I'm speaking more about Java here. There are several really nice shells for Java that let you interact with objects. I have used BeanShell to interactively test my Java objects.

When you start using these big frameworks and you have classes that really can't operate on their own, an interactive shell won't help too much. A less-intrusive framework that lets you work with POJOs (Plain Old Java Objects, for any non-Java people out there) helps tremendously. I wonder sometimes if the presence of all these annotation languages and code generators might indicate that Java isn't flexible enough on its own.

Jumping back to static typing for a minute, I know there has been a lot more debate about it recently. One of the points that several people have brought up (Bruce Eckel & Robert Martin, for example) is that static typing helps you eliminate errors where you use the wrong type somewhere. If you are thoroughly testing your code, those errors should shake out anyway. If they don't, it might indicate that you aren't testing well enough. That sounds good in theory, at least.
 
Toby, thanks for your insight. I have read some of your comments on theserverside.com as well and you are very good at getting your point across without being a jerk. That seems rare these days.

I'm having a hard time getting a handle on what you're talking about here... J2EE maybe? I'm really not sure how that affects what code snippets you're going to write in a transcript window. As a side note, I prefer working with "POJOs" myself, which is why I prefer JDO for persistence.
Yes, I was speaking about J2EE. A lot of the projects I have worked on use EJB and rely heavily on XDoclet to generate additional classes and such. My comment was born out of the frustration of not being able to interact with those classes directly. It seems like some of the newer frameworks are more Pojo-oriented.

I think that I tend to agree with you with respect to static typing and larger projects. The appeal of dynamic typing is that you don't waste time with all these extra declarations about types. For a small program, expecially a throw-away, that trade-off is reasonable. For code that may be used for a long time, by many teams, the time you spend writing type declarations is no longer a waste.

If I need to process a file quickly, it's hard to beat Python's:
for line in file("foobar.txt"):


The more I look at Nice, the cooler it looks. The nullable option thing looks a little odd to me. I've never been that fond of character annotations on variable names, especially at the beginning. That's more of a nit, though. I was also surprised at the lack of explicit casting, but it looks like an interesting solution.

As far as contracts go, I see pre & post conditions, are invariants not useful? Eiffel has them and I always thought they were an interesting idea, but wondered how practical they are to implement.

As I was writing this, it occurred to me that it would be easy in Nice to write a function to do the equivalent of Python's "for line in file('xx'):". I'm definitely going to have to play with this language some more.
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?