Monday, February 14, 2011

Macro or not to macro?

Today I was wondering about the code I commented about some days ago. I essentially created a function to transform an array of objects I got in input to something more easy to deal with, that is to say an hash-map. Since most of those parameters needed to become properties of the object receiving them, I considered that I could just write a function to avoid repetitive code.

The function approach is useful and it solves my problem. I need a dictionary, I get a dictionary. However, what I am doing is essentially akin to binding/destructuring. I only have to rely on the information on the left side of the binding, as the right side completely lacks such information. Moreover, in some situation it is nice to directly access the "fields". I definitely decided that it was time for a macro.

Notice, I'm quite against macro abuse. I've already abused many nice programming features as soon as I learned them. Consequently I know I can resist this time. But this is definitely too nice not to use a macro. My first attempt was this:



It essentially retains the same structure of the function, but is a macro. It is slightly more robust because it makes clear what happens when the array is too short (the variables are nil) or when it is too long (variables are ignored). It stresses that what matters is just the keys parameter, while the defaults only help. In particular it is clear that having keys in the defaults that are not among keys is completely useless. Consequently, order in defaults is plainly irrelevant and plain dictionaries (instead of array-map's) can be used.

However, when I started substituting the code in the project, I felt that the code did not look particularly nicer. I immediately started thinking to a new solution. An afterthought is that my typical use-case (get a dictionary to merge with .state) is not easily covered.

In "Artificial Intelligence Programming: Case Studies in Common Lisp" some suggestions regarding macros are given at the very beginning of the book. Norvig explains that writing a macro is a four-step process:
  1. Decide if the macro is really necessary.
  2. Write down the syntax of the macro
  3. Figure out what the macro should expand to
  4. Use defmacro to implement the syntax/expansion correspondence
I felt like the syntax of the macro was not carefully designed. The advise given in the book (a part of not writing them) is to follow established Lisp conventions for macro syntax whenever possible. In this case, the idea was to follow clojure conventions. In particular, clojure has structural unbinding. Using the {:keys [...], :or {}, :as options} I just have all the features I need.

I considered that in order to read the other macro a programmer needed either to look at the implementation or to the documentation. On the other hand, following clojure conventions, the meaning of the code would be apparent to everybody knowing about clojure destructuring.



That is an example on how to use the macro. And now, the macro itself!



Not perfect, but it does its job.

, , ,

2 comments:

Alfredo said...

I have still so much to learn :S

Unknown said...

Why do you say so?