Re: Why want to do the things Lisp is best at?

From: Jeff (jma_at_insightbb.com)
Date: 08/18/04


Date: 18 Aug 2004 09:13:47 -0700

The accumulator problem that Graham writes about is a silly problem. It
is designed to show how Lisp shines in one particular area. There are
quite a few things that a language requires to be able to write that
problem to his specifications. Each language typically has a "catch"
program that shows extremes. Take a look at quicksort in Haskell. Or
the washing machine example on Forth, Inc.'s website.

My two favorite languages to program in are Forth and Lisp. Both are
wonderful languages. Neither could replace the other in what they do
best (I would never think of using another language for embedded
programming other than Forth). However, both languages are exception
for one shear fact: they are extensible. This cannot be stressed
enough.

IMHO, the "aha" moment with any language or concept happens when you
actually force yourself to use it. My "aha" moment with OOP occured
when I was a teenager writing my first MUD. I realized that a bag was a
container, and so is a room, and so is a living object... and a player
is a living object, and so is a monster.

With Lisp, I really had two "aha" moments -- both occurred during the
same project, but at different times. The project (which I hope to be
publishing on the web soon) was a regular expression parser. It started
out as a C in Lisp piece of code. But as I kept refining it, and
realizing what could really be done, it was absolutely amazing how
small and fast the code got. Extremely simple to understand, debug,
test -- I can't stress this enough.

An example of this was when I got to compiling the regular expression.
What turned out to be the simplest solution was to create functions at
runtime for each component of the expression, and then join them in a
list. To compare the regular expression to a stream, I just call each
function in the list one at a time with a stream, if all the functions
pass, then there was a match. Try doing that in C/C++.

What was an even bigger "aha" was when I wanted to add features to the
regular expression parser. What happens with a | symbol in a regular
expression? Well, just create a new list of functions (basically
another regular expression), and now join both compiled lists into a
list of 2 expressions. This was something that wasn't in my original
design spec to do, but I realized that it would only take about 5 lines
of code to add it.

My second "aha" with Lisp came when I really started using macros. I
had used them (mildly) throughout the regex parser, but they were just
1-2 line macros to simplify life. However, when the parser was done, I
needed to use it to create a FLEX like parser that could be used to
parse another language file. As it turns out, this macro ended up being
only 25 lines of code, and allowed me to write a function like so:

(setq token-parser
(regex:flex
(#/if|then/ (lambda (text)
(format t "KEYWORD: ~A~%" text)))
(#/\a\w*/ (lambda (text)
(format t "SYMBOL: ~A~%" text)))
(#/0x\x+/ (lambda (text)
(format t "HEX: ~A~%" text)))
(#/\d+/ (lambda (text)
(format t "DECIMAL: ~A~%" text)))
(#/("[^"]*")+/ (lambda (text)
(format t "STRING: ~A~%" text)))
(#/,/ (lambda (text)
(format t "COMMA~%")))
(#/\s+/ nil)))

This then creates a very large function for me that checks, in order,
each regular expression an parses the text. For example:

(FUNCALL token-parser "if x then print \"HI!\",10,0xff")
==> KEYWORD: if
SYMBOL: x
KEYWORD: then
SYMBOL: print
STRING: "HI!"
COMMA
DECIMAL: 10
COMMA
HEX: 0xff
T

The best advice is to use it. I'd ignore more "examples" that you see
in a book as propoganda. (Note: Paul Graham is an excellent writer and
I love Hackers and Painters). Since you seem to be an accomplished
programmer, take a real world problem you needed to solve and now solve
it using Lisp. Take advantage of high order functions and lists -- I
bet you'll be pleasantly suprised at how differently you will solve the
problem, and how much simpler the solution will be :)

Jeff


Loading