Wednesday, December 5, 2007

Defining new language constructs

I was recently involved in a conversation about the flexibility of different languages. Once a person has used a language like Smalltalk or Lisp, one measure they will probably use will be how hard it is to add new constructs to the language that behave as "built in" ones.

In Smalltalk this is easy because all constructs are written within the language using blocks (which serve the purpose of "closures"), so any methods you define can easily behave as standard constructs that come with the language. It also helps that you can add your methods to any class, including existing ones like True where certain operations make more sense.

In other languages this takes more thought because the constructs don't use explicitly delayed execution the way Smalltalk does. For example the lisp code:

> (if (some-function) (true-case) (false-case))

may look like a regular function call but the last two forms don't get evaluated until the first one completes, at which point only one of them will be.

Now lets imagine in one of our projects we are seeing a great deal of code that looks like:

> (if (and (case-one) (case-two))
> (true-case)
> (false-case))

but perhaps it would be clearer to make a new construct called 'ifboth. E.g.

> (ifboth ((case-one) (case-two)) (true-case) (false-case))

But how can we do this? We can't use a function because that would evaluate all these forms before calling our new construct. Luckily Lisp comes to the rescue with macros. A macro receives the forms unevaluated as lists, and is free to do what it needs with them. It simply needs to return a list that will be the expanded code. In our case the macro is pretty simple:

> (defmacro ifboth ((case-one case-two) true-case false-case)
> `(if (and ,case-one ,case-two)
> ,true-case
> ,false-case))

I wont go into the details of how this works as there are plenty of resources on the subject. The above simple takes code like:

> (ifboth ((case-one) (case-two)) (true-case) (false-case))

And turns it into code like:

> (if (and (case-one) (case-two))
> (true-case)
> (false-case))

at compile time.

But this got me thinking about Haskell. Can Haskell do this? They don't have macros after all. 1

It turns out Haskell can. Lisp has to use macros because it needs a way of controlling the evaluation of the forms. 2 In Haskell forms are always delayed until actually used. So how can we make ifboth for Haskell?

> ifboth :: Bool -> Bool -> a -> a -> a
> ifboth True True trueCase = trueCase
> ifboth _ _ _ falseCase = falseCase

And test it in ghci with:

> ifboth True True (print "hi") (print "bye") {- prints "hi" -}
> ifboth True False (print "hi") (print "bye") {- prints "bye" -}

If one spent a while programming in Haskell with it's lazy evaluated, "only evaluate if the value is actually used" nature, one might forget that defining new language constructs actually requires extra thought in some languages. And isn't even possible in others.

1. Actually they do have several different implementations of "macro"-ish systems, but they are not needed for things like delayed evaluation as the language simply works this way.

2. Of course Lisp macros have many uses beyond this, but the delayed evaluation is handy in this case.