Review: Parsing Techniques: A Practical Guide

For Christmas this year, I received a shiny hardback copy of Parsing Techniques: A Practical Guide by Grune and Jacobs. It's a thrilling book, if you want to learn parsing, which I do.

Where most books proceed in a sort of linear fashion, this book teaches parsing in layers. First you learn what a grammar is. Then you learn what it means to parse: what's a parse tree? What's bottom-up vs. top-down? What's a leftmost vs. rightmost derivation?

Next you get some general ideas and methods for parsing, e.g. CYK and Unger, and then you dive into the implementations of parsers (in pseudocode and in C) in great detail. This is about as far as I've gotten so far, before having to go back and figure out what the heck I just read. But it's an interesting progression. Reading the book, I feel like I'm constantly revisiting things I learned a few chapters ago, but this time in more detail. The book kind of does a breadth-first traversal of the world of parsing.

Be warned however: this book is not easy reading. It's dense, heavy on the info, light on the entertainment. Unless you really get a kick of out parsing, this will probably put you to sleep if taken in large doses. But it is a trove of information, and I couldn't put the book down during certain chapters.

In fact there's so much information in this book that it's almost depressing. The bibliography alone takes up 1/4 of the book, and lists 1,500(!) authors. It'd take me a week to read the bibliography, and probably many years to read every book listed there. Parsing could easy consume a lifetime of study, and I'm saddened that I'm probably never going to find the time to master all there is to know. But such is life.

If I had one quibble with this book, it'd be the same quibble I have with most math papers. The notation is horrible. Say what you will about programmers, most of us know that code is written for humans, not for machines, and we give our variables descriptive names. In math it's all single letters variable names.

When the authors of this book run out of single letters, they use letters with bars over them, or bold letters vs. normal typeface letters, or they do things like this:

...whenever a non-terminal A is entered in to entry Ri,l of the recognition table because there is a rule A -> BC and B is in Ri,k, and C is in Ri+k,l-k, the rule A_i_l -> B_i_k C_m_n is added to the parse forest grammar, where m = i + k and n = i + l - k.

This is the first paragraph of a section. Those variables are not mentioned before this sentence. This is certainly not a style of writing that I'm used to reading. It takes me a good dozen tries to understand. (Using lowercase i's and l's right next to each other should be prohibited by law.)

In any case, this book is good. One of my favorite tools has always been Perl-style regular expressions, and I feel like this book has expanded my understanding of how they work. Learning to write a recognizer, learning how things are implemented under the hood, you couldn't ask for a more interesting topic. I can't wait to try writing a toy parser generator or regex recognizer in Clojure once I've solidified my understanding of some of these concepts.

Technology ain't everything

Let's discuss can openers.

Growing up, my parents would often invest in electric can openers. These things never worked. Some of them sit robot-like on top of the can and walk themselves around the top while chopping the metal. Some of them were mounted on the wall and you somehow get the can to hang in a harness while the device spins the can around. It takes a PhD and double-jointedness to get the can set up in these devices properly. And then you push a button, a lot of noise happens, and usually the can ends up half-open, half-bent up to the point where it's un-openable short of dynamite.

When I open a can, I use one of these. You jam the metal bit into the can and turn the crank, the can spins in a circle and 10 seconds later, off comes the razer-sharp top. The one I own was probably manufactured in the 1980's and it's still sharp enough to open a can with minimal effort.

Is it really that hard to turn a handle for 10 seconds? Do we really need computer-controlled robotic can-opening devices?

Consider books. I still buy and read all of my books in the form of compressed wood pulp. There are newfangled e-book readers, but I don't want one. Why? Because the only places I read are 1) In the bathtub, and 2) Lying in bed. Taking a computer into the bathtub is generally not a good idea, and holding a Kindle above my head for 3 hours is awkward compared to lying a (3-D) book on the bed beside me with one page bent up so I can read it. (Note: I have dropped a book in the bathtub on more than one occasion, and contrary to my expectations, once it dried it was still perfectly readable, no ink runnage at all.)

I know some day, maybe soon, paper books are going to be gone and we're all going to read books from digital devices. But I like my books. I know there are benefits to having electronic books instead of paper ones. But even though they're a waste of space, even though they can have pages ripped out, even though they can burn up or smudge or age and become brittle, I like paper books better.

Mostly I like paper books because they're simple, analog devices. I don't have to mess with any kind of user interface. Books don't have battery life. Books don't have copy protection. Books don't require me to sign up for user accounts at some website and worry about having an internet connection. I can flip through the pages with my fingers. I can tell how many pages are left by the thickness of the pages that are left. I have actually never comfortably finished a long e-book, not even books about programming, where you'd think the ability to copy/paste code would be a boon. I'll pay good money for a paper copy of a book even if the electronic version is free.

This is probably the most banal thing I've ever written about. But there is such a thing as too much technology. I say this as a person who spends all day trying to get people to use databases instead of keeping drawers full of paper records. Technology for the sake of technology is a waste of time.

2009 in review

2009 sucked because I was living in a different country than my wife, thanks to months of Canadian immigration paperwork and bureaucracy. This situation is going to be changing in the immediate future, which means 2010 will not suck.

I did have a lot of time to learn things, which is good. I got all kinds of things accomplished at work, learned some supervisory skills (shudder), wrote some code that was put to good use etc. My websites grew in popularity slightly. I learned Clojure and had lots of fun banging out a few apps. I tried to learn Haskell and failed. I feel like I advanced in origami a bit. I inched ahead slightly learning Japanese. I figure in another 50 years I'll know Japanese enough to say "Hello, I know Japanese but I'm too old to use it for anything now".

I read a gratuitous amount of books. I got into Asimov for the first time; usually I dislike sci-fi, but his stuff is good. I found Neal Stephenson, and wish I'd have found him earlier. I read more programming books than I can remember. I found some interesting books on psychopathy and other psychology-related topics, and read plenty of Richard Dawkins and other sciency and atheismy books.

There just isn't enough time in the day to learn everything I want to learn. I come home from a day of writing code all day at work, goof off on the internet a bit, talk to my wife, and then I read books and write code until 3 or 4AM, and it's still not enough time.

I have apps I want to code, drawings I want to draw, origami I want to fold, video games I want to play, movies I want to watch, music I want to listen to, and the list of books I want to read keeps growing faster than I can read them, even given that I already read 4 or 5 books per month. If I had a social life, I can't imagine how little time I'd have for these things.

This year I almost want to slam the brakes on, spend a lot of time with my wife, and let my brain settle. I will definitely do that to some degree, but I can't stop learning in the meantime. I'm running out of years. 29 years old, only four or five good decades left, if I'm lucky, and my brain will be deteriorating the whole time. At least I have plenty to keep me busy.

Let's parse

Is there anything more fun than parsing strings? I submit to you that there is not. I'm currently reading my way through Parsing Techniques - A Practical Guide, which has a first edition free online. (I'm hoping Santa brings me a copy of the 2nd edition this year.)

This is a good book, with enough math to be rigorous but not so much that it's completely unreadable. It starts from the absolute basics ("What's a grammar?") and goes through the Chomsky hierarchy and then dives into parsing techniques in great detail, in a language-agnostic way.

Languages and grammars are fascinating. In high school I studied Spanish, French, Latin and German, largely in my spare time. When I was 16, if people asked what I wanted to do for a living, I said "translator".

The plan to become a translator failed partly because the quality of my early education was horrendous and partly because mastering a language is extremely difficult and at 16 I wasn't motived enough. And then computers showed up in my life, which gave me a never-ending supply of languages to play with, while being fun (and profitable) in so many other ways. But I still took two years of Japanese classes in college for no reason other than enjoyment, and I'm still trying (and failing) to learn Japanese in my spare time 8 years later.

Perl was my first favorite language probably for no reason other than regular expressions. I can understand how people call PCRE syntax line-noise, but to me it's beautiful line noise. I live and breathe regular expressions nowadays. My favorite CS class in college was one where we went through and laboriously built finite-state automata and pushdown automata and Turing machines. Seeing the equivalence of these simple machines with the different classes of grammars was a huge epiphany. Such a simple concept with such huge consequences.

Dijkstra said:

Besides a mathematical inclination, an exceptionally good mastery of one's native tongue is the most vital asset of a competent programmer.

I strongly agree with that sentiment. People tell me at times that I'm good at written communication. I have my doubts, and anyways I find it funny because I'm so terrible at verbal communication. I think if I have any success at writing, it's because I view writing as a mechanical process.

I told a prof in college once that I felt like my papers wrote themselves once I had an idea in mind. There are rules of grammar and style, and you learn them and follow them, or break them deliberately if you have a good reason to. You write some prose, then you debug it until it "works" mentally. I don't care about typos and I split infinitives and comma-splice on purpose, but ambiguous or awkward phrases usually stand out to me like compiler bugs in my brain.

What's more important than language? Few things. Language is important enough to be nearly hard-wired into our brains. Children learn it instinctively. Human beings can still easily and effortlessly out-perform the best supercomputer at the task of parsing and interpreting speech. We think in words. The programming languages computers understand are dirt-simple by comparison, but writing code still feels like writing "thoughts for the computer" sometimes.

There are very few times you'll hear me say "What a wonderful world we live in". But one of those times is when I have the opportunity to explore an area of study like language. It's such an enjoyable experience to struggle and try to master such a thing. It's an amazing universe where we have these weird little rules and they work and we can understand them and manipulate them and produce things with them.

Review: Coders at Work

Recently I received a preview copy of Peter Seibel's newest book, Coders at Work.

This is a wonderful book if you are a programmer and care at all about the art, craft, and/or science of programming. If there is any wisdom to be had in this fledgling field of ours, this book contains buckets of it.

The book consists entirely of long interviews with some big names in the world of programming: Donald Knuth, Ken Thompson, Jamie Zawinski, Guy Steele, Peter Norvig, the list goes on. There are also some names I wasn't quite so familiar with (but maybe should have been), like Brad Fitzpatrick, the inventor of Livejournal.

But everyone interviewed for the book has produced some grand, successful project in their time. These are tried-and-true, battle-tested programmers and in this book they share their war stories and advice.

Real Confusing Haskell

I can pinpoint the exact page in Real World Haskell where I became lost. I was reading along surprisingly well until page 156, upon introduction of newtype.

At that my point my smug grin became a panicked grimace. The next dozen pages were an insane downward spiral into the dark labyrinth of Haskell's type system. I had just barely kept data and class and friends straight in my mind. type I managed to ignore completely. newtype was the straw that broke the camel's back.

As a general rule, Haskell syntax is incredibly impenetrable. => vs. -> vs. <-? I have yet to reach the chapter dealing with >>=. The index tells me I can look forward to such wonders as >>? and ==> and <|>. Who in their right mind thought up the operator named .&.? The language looks like Japanese emoticons run amuck. If and when I reach the \(^.^)/ operator I'm calling it a day.

Maybe Lisp has spoiled me, but the prospect of memorizing a list of punctuation is wearisome. And the way you can switch between prefix and infix notation using parens and backticks makes my eyes cross. Add in syntactic whitespace and I don't know what to tell you.

I could still grow to like Haskell, but learning a new language for me always goes through a few distinct stages:

Curiosity -> Excitement -> Reality Sets In -> Frustration -> Rage ...

At Rage I reach a fork in the road: I either proceed through Acceptance into Fumbling and finally to Productivity, or I go straight from Rage to Undying Hatred. Haskell could still go either way.

Books are our friends

I went on a reading binge over Thanksgiving break. Read on for reviews.

Test-Driven Development By Example

First I read Test-Driven Development By Example. This is a very short book, especially given the hefty price tag. I would probably not have bought this if I'd seen it on a shelf at the store rather than online.

That said, it's a good book if you want to understand the mindset behind the whole test-driven development fad. The book is heavy on mindset and light on mechanics; it doesn't tell you how to set up an environment, how to compile and run tests, or any such thing. It just goes through some examples and explains what the programmer was thinking, and how to tackle the problem from a TDD point of view. The intended audience therefore is someone who has plenty of experience programming and wants to learn a new way to look at problem solving.

The book goes through writing some terrible code (admittedly terrible by the author) to solve a simple problem, then refactoring the code to be less and less terrible by means of tests. The writing style is engaging and casual and he describes mistakes as well as successes, which is a nice way to write a tutorial book. The alternate writing style, belting out the correct answer the first time every time, is not as enlightening, and I appreciate it in this book.

The book places a heavy emphasis on writing tests FIRST, making small incremental changes, and refactoring as you go. It also explains how TDD can be related to various OOP design patterns, which I didn't find all that helpful. And it describes some "test patterns", which I found even less helpful. But it's mostly an aesthetic objection; something about "design patterns" sits less and less well with me the more I get away from Java-like languages. The information is still good, straightforward and to the point, take it or leave it.

I have been unable to drink the TDD Kool-Aid, but I can see where it'd probably result in an improvement in some of my code for certain specific kinds of problems. I'll probably try it out again next time I have to write a little library at work.

This book is expensive and short, but the alternative for learning about TDD are half-baked blog posts, which is why I bought a book. I don't know of a better book because this is the only one I have, and I'm unsure at this point whether I really want to buy another book on this topic. But this book is highly recommended by many TDD fanatics enthusiasts, so I don't know.

The Little Schemer

Next I read The Little Schemer. The style of this book can best be described as "cute". Examples all use food, and it instructs you at various points to go make yourself a sammich (even leaving room in the book for "jelly stains"). This somehow makes the topics, which can be rather intimidating, somehow less scary. The book is very short, and not very dense (not many words on a page), but they somehow cram a lot of information into the book anyways, via the many examples. It's a very focused and methodical book.

This book goes through developing a simplified dialect of Scheme, with a focus on recursion and building up parts of the language from very simple primitives. There are some neat little things along the way, like using s-expressions to represent numbers, and building up a full set of arithmetic functions using nothing but "add 1", "subtract 1", and recursion. It'd be insane to do in real life because of performance, but it's really neat from a "hey look what you can do, isn't this cool!" point of view.

The first half of the book is pretty simplistic (at least for me, with some basic knowledge of Lisp and Scheme), but the second half really starts delving into some crazy things, passing lambdas around to lambdas and writing evaluators and stuff. But you almost don't even notice because of how solidly the foundations are built up to that point, and how well the examples are explained. (You probably will notice once you hit the y-combinator, on account of your brains detonating.)

This not a good book for "learning Lisp" in terms of learning the gritty details of to use a real-life implementation of Lisp. But it's a good book for learning some of the concepts that make Lisp and functional languages powerful. This is a very interesting and unique book, also highly recommended by many in the Lisp world. I'm already planning to try The Seasoned Schemer next.

Real World Haskell

Finally I started plowing through the recently-released Real World Haskell. This book is freely available online, which is great. It also allows readers to make comments on every paragraph in the book, which is a highly collaborative and probably very intelligent way to write a book, and also can provide insight for readers who don't understand the text at any given point. I may buy a hard copy, I haven't decided. (The online version has some FIXME notes and typos that I can only hope are fixed in the real thing.)

I'm only up to chapter 11, but it's good so far. It gives a ton of examples and goes slow enough for people completely new to Haskell to pick the language up. And it includes some of the mechanics of compiling and/or running programs in GHC, which is extremely helpful. I did start getting lost around the time monads were introduced, but that's to be expected. It usually takes me two or three good reads of this kind of book before I grok it all, which is no fault of the book.

Haskell. Last time I used it, I wrote Pong in Haskell in one of my college classes. It was an immensely painful experience. I'm going back to re-learn Haskell now because I find myself edging more and more toward functional languages and away from the C/Java world.

Haskell is far too much to swallow if you have no experience with functional programming, which is probably why I hated it in college. (That said, a smarter person than I may have fewer problems.) After graduating school, I got my first whiff of this world again via Perl's map and grep and friends. Then Ruby pushed it home for me with its widespread use of block parameters (lambdas in disguise). And after learning Lisp (and especially later, Clojure) I can't live without higher-order functions. If I had to write a classic for-loop and manually keep track of a counter variable I'd probably vomit at this point. Haskell takes function manipulation to an even greater extreme, which I still haven't fully wrapped my head around, but I like what I've learned so far.

Laziness is awesome, function currying is awesome, and this is the first time I've read about folding, apart from brief struggles with Ruby's (poorly named) Enumerable#inject. Pattern matching strikes me as vaguely similar to Common Lisp's / Clojure's destructuring-binding, which is one of the nicest features I've seen in any language when it comes to making a programmer's life easier.

Maybe it's all just interesting because it's still new to me. But I like the whole idea of functional programming. I like the idea of functions that always produce the same output from a given input. How and when to handle side-effects and object mutation is a problem that's always nagged at the back of my mind even when writing Ruby code.

That said, Haskell still (at this point in the book) does not strike me as a practical or real-world language in the slightest. You've got to jump through some crazy hoops to get a lot of things to work, especially when it comes to I/O. The book describes how to write a pretty-printer, and how to parse a binary file, both of which require some acrobatics to write concisely in Haskell. In particular the parser library lost me entirely; functions were being chained to functions in all directions and I couldn't follow it.

I think one reason Java is so popular is that you don't have to be a genius to write it. To write Haskell (or even Lisp) well, to take advantage of the language and use it smoothly, you really do need to do some deeper thinking. The abstractions are more powerful and more "abstract". It's not too hard to understand a world made of objects with a bunch of knobs you turn via method calls. Lambdas and recursive functions and monads and typeclasses are a more ephemeral thing.

That said I plan to stick with the book. Practice makes perfect.

PAIP review

It's been a while since I acquired PAIP. I can't say I've read all of it over the past couple months, but I've read most of it.

The initial chapters that give an intro to Common Lisp seem largely useless to me. There are better books to introduce Common Lisp (especially now that we have PCL). Norvig admits as much himself, and thankfully doesn't devote much of the book to intro material.

Once you get past those, this book is densely packed with information. Just tracing through and understanding the source code would probably take me a couple of months, let alone grasping all the concepts the source code is trying to exemplify. It's a wonder to me that one person can produce this much material. But the downside is that it's a bit difficult to read at times; it's kind of dry and reads like a text book. In spite of that, I was glued to this book for quite some time; the information inside is engaging enough to make up for the lack of presentation.

Some of the examples of code in the book really are quite startling. For example Norvig writes a pattern-matcher that implements some subset of Perlish regular expressions, and it only takes a couple pages of code. Later in another couple pages of code, he implements a variant of Prolog. At one point he writes some pattern-matchers that can parse a surprisingly large subset of the English language. But a lot of other examples of code weren't quite so interesting to me personally; a lot of the material is dedicated to tree and graph search algorithms, which brought to mind long boring lectures from my college days. (As a side note, it's very sad how much of the history of AI can be reduced to "clever graph search algorithms". I don't know much about AI today but I hope it's advanced a bit past that.)

This book is from 1992 and some of it does feel a bit dated. This book was apparently written just after Common Lisp was standardized. Object oriented programming was apparently relatively new back then, and Norvig glosses over CLOS. Most of the code is written in functional style. However Common Lisp today still doesn't force OO on you by any means, and a lot of Lispers today apparently still don't care much for OO, so the code is more relevant than you'd imagine.

And as you can't help realizing after reading an old CS book or two, CS as a science hasn't seen all that many breakthroughs in the short couple of decades it's been around. It's really quite sad in a way that a book written over 15 years ago can still be so relevant; we have all these huge advances in computer hardware, yet it still feels like we're in the Stone Age of computer programming.

Not being an Artifical Intelligence person myself, I can't judge how well this book serves as a guide to AI. But Norvig's stated goal was in large part to show the history of AI, and he does that very well. (This was history back in 1992 when the book was written, so it's even older now, but still interesting.) He walks through implementing a lot of famous AI programs from the past, and explains their limitations and how they can be improved. Want to write ELISA or other chat-bots? He does that. Want to write an Othello-playing program? He does that too.

One of the best things about this book is that Norvig's code is just about the Lispiest code I've seen. He does many things that take full advantage of all the nooks and crannies of Common Lisp and wouldn't make much sense in other languages. This is a good book to help you think in Lisp.

So I recommend this book. Though I'm unsure I'll ever manage to absorb most of the information in it.

PAIP, review one of probably many

I got through the first four chapters of PAIP yesterday and today. It's good so far. I had reservations about spending $80 on a book, but it's 950 pages and almost all of it is good stuff, from what I can see at a glance. Little space is wasted on introduction material or tutorials for people who know nothing about programming. He dives right into the good stuff in chapter four.

Already I've picked up a few nice bits of info, interesting standard functions / macros like TRACE which is good for debugging. And SUBLIS which takes a list of (key . value) sublists and replaces all the keys with all the values, in some other list. Common Lisp is such a freaking huge language, in terms of its standard library of functions. Good thing or bad thing? It may be a bad thing if not for the fact that you load Lisp once and it persists forever.

Norvig makes an interesting point about the REPL. What do most programs need to do? Take input from the user, do something with it, and give output back to the user.

To get input, your program can read from command-line args, or prompt to STDIN, or take it out of text fields in a QT app, or read it from a config file. But it's generally always strings. So you get some strings, and then what? You have to figure out what the strings say, and either turn the strings into something usable or use the strings to figure out what action to perform. So you may have a dedicated library to parse command-line args and a dispatch table to map them to function calls. Or you may match the strings against a regex and depending what the strings look like, turn them into your language's representation of an integer or float using some "parseInt" or "parseFloat" functions. Or if your strings are XML, you may parse them into some big funky XML structure, then access bits of that structure to figure out what to do.

In Lisp on the other hand, you happen already to have a powerful reader/parser at your disposal: the REPL itself. It already knows how to read string representations of lists, numbers, pathnames, and many other things, and it can turn them into Lisp objects for you with no effort on your part. If you were using Ruby or Perl, short of evil eval, you wouldn't get anything close to that capability in your program.

What's more, the REPL knows how to read string representations of Lisp code (new functions and function calls etc.), and evaluate the code and end up with Lisp objects. It would be like if you wrote a C program which prompted you for input, and the input you gave could be a string representing a new C program that when run would produce a string that contained your input (or produce in-memory C structures your C program could use). So the main program would call GCC and compile and run your input to get results and use those results as its final input. Except that still wouldn't be nearly as powerful as what Lisp is doing.

So if you can figure out how to shape your input into a form that the Lisp reader can read, you're set. And it so happens sexps are a great representation for a great many things. Maybe this is part of why Lispers seem to worship the REPL and shun compiling their apps into command-line, standalone executables? Maybe this is part of what's meant by the oft-quoted-to-death Greenspun's 10th Rule?

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.

Much more PAIP to follow. It should be enjoyable. There's nothing like a good book.

Functional programming hurts me

This post will probably come back to haunt me, but functional programming doesn't sit well with me. I'm trying again to read through SICP. Last time I tried, I lost track of it and gave up after a few chapters. This time I hope to persevere, and I'm trying to go more slowly through the examples to really understand things.

I was trying to do some of the exercises in chapter 1 today, and pretty much bombing on all of them. Most of the exercises I've seen so far in SICP sound like they came out of a math book. "There is a clever algorithm for computing the Fibonacci numbers in a logarithmic number of steps..." Uh huh. This is not the kind of problem I enjoy, or have even the slightest use for. But maybe the problems are only pointless examples to illustrate a point, like "mass-less friction-less pulleys" in physics textbooks. I can live with that I guess.

Here is a Scheme example from SICP to compute b to the power of n:

(define (expt b n)
  (expt-iter b n 1))

(define (expt-iter b counter product)
  (if (= counter 0)
      product
      (expt-iter b
                (- counter 1)
                (* b product))))

This is presented as a wonderful and novel functional programming approach.

One of my problems is the amount of brain-work it takes to understand this. OK, start with EXPT. We take b and n, and then call some recursive helper function with b, n, and a state variable product initialized to 1 HEY WAIT A SECOND THERE'S A STATE VARIABLE?!

This is one way to cheat in functional programming, I guess. product is a variable name / symbol, which describes "something" that takes on different values over time, each value representing the same "thing" in different states. That qualifies as a state variable to me. The only difference is that the state variable is actually N different variables (with the same name) splattered across a bunch of frames on a call stack, instead of an abstraction of a single simple bucket where I dump a value.

Now in EXPT-ITER, counter starts at n, and every time the function calls itself, counter decreases by 1, until you reach 0, at which point EXPT-ITER stops calling itself and returns something. So this is a perverted, inside-out way of saying "do something N times", where every iteration costs me a recursive function call. Nice.

OK, what are we doing N times? We're multiplying product, which starts at 1, by b, which then becomes the new value of product next time around.

SO WHY NOT JUST WRITE THAT TO BEGIN WITH?

(defun simple-expt (b n)
  (let ((total 1))
    (dotimes (i n)
      (setf total (* total b)))
    total))

In Common Lisp this reads as "Let total start at 1. N times, set total to the product of total and b. Then return total." This is EXACTLY what the Scheme version is doing. Except I don't have to turn my brain inside-out to write it down this way. The CL code reads exactly like what the program is doing.

When all you have is a hammer, every problem looks like a nail. When all you have is recursion, every problem looks like it should be solved using recursion, I guess.

Actually if I ignore the functional programming bits, SICP is a great book. There are a lot of good things in there. But (so far) Scheme isn't one of them.