Monday, July 28, 2008

Stemming, Part 12: Macros and Moving on

All right, let’s review what we’ve been doing for the last few days.

What Have We Done?

In a real sense, we’ve been writing a program that writes programs. A macro is
a function that accepts Clojure code—represented as lists, vectors, symbols,
strings, numbers, and other Clojure data types—and returns another list,
vector, etc., that represents Clojure code. A macro can change its input any
way it wants to, but of course the output needs to be valid Clojure code.

Creating Abstractions

All computer languages allow us to make abstractions. We can abstract the data
“given-name,” “surname,” “age,” and “address” into a class (or structure in
Clojure) called “Person.” We can abstract the action of printing “Greeting”
and a person’s name into the function “hello-world.” All languages in common
use let us do that. Object-oriented languages also give us other abstractions
for associating actions with data.

But lisps, including Clojure, go a step beyond that. They allow us to create
abstractions of the syntax of the language. This allows us to control when and
how expressions get executed.

Does this allow us to do things we cannot do in other languages? Strictly
speaking, no.

But it allows us to do things more concisely and readably than we otherwise
could. But because we can abstract more things away and worry less about them,
we can build and understand programs that are more complex than we otherwise
could be able to comprehend.

Domain Specific Languages

Another benefit of macros is that they allow us to create a mini-language with
Clojure. If you look at Clojure’s source code, most of it is written in
Clojure. And you have that same power. Once written, your code isn’t really
that different than the code that makes up Clojure’s core.

You can use that ability to extend Clojure, to build up from its foundation,
to create your own language ideally suited to the work you need to do.

You can do this in other languages, of course. It’s called creating domain
specific languages. But in lisp, it is natural in a way it is in no
other language.

The Spiderman Clause

Of course, as Peter Parker was told, “With great power comes great
responsibility.” Like operator overloading or multiple inheritance, macros
make it easy for you to shoot yourself in the foot. If you’re not careful, you
can create something that is so far from Clojure that others have trouble
understanding it, and you will have trouble getting your bearings when you
come back to it in six months.

Remember: a little macros go a long way.

In the next posting, we’ll finally start on the steps involved in stemming.