Posts Tagged ‘Macros’
Macros Design Patterns
I was reading the thread What are some fun or useful macros? on reddit and it reminded me of another thread that appeared in the Pro mailing list. These kind of threads are always enjoyable because each time you learn something new and see really interesting things. While reading them, it crossed my mind that another variant of this question would be what are some fun or useful macros design patterns. Instead of examples of specific code macros (general or not) it would be nice to see common programming practices using macros. So, for a lack of a better expression name, let’s call them macros design patterns.
My favorite one is to configure an algorithm, especially when we want to use the correct types. Essentially, we write an algorithm using a macro that takes types or configuration arguments and then we expand it to the appropriate desired configurations. For example, if you have an algorithm that operates on different types of sequences, instead of writing several duplicate functions with the same algorithm but with the associated type declarations, just apply this pattern. A simple but contrived example: we want to compute the mean of a vector but use its proper type. In addition, we might also want the possibility of using a key to access the vector elements. We can write the following macro:
(defmacro mean-body (vector vector-type vector-ref key)
(let ((size (gensym)) (i (gensym)))
`(locally
(declare (type ,vector-type ,vector))
(let ((,size (length ,vector)))
(/ (loop for ,i from 0 below ,size
sum ,(if key
`(funcall ,key (,vector-ref ,vector ,i))
`(,vector-ref ,vector ,i)))
,size)))))
The macro contains the algorithm (in this simple case the mean) and the arguments allow us to configure the multiple versions we need. If we want a simple-vector, the macro will expand to use the correct type declaration and svref. If a key function is needed it will also include it. Then, we can call the macro with the several configurations value inside the main function:
(defun mean (vector &optional key)
(typecase vector
(simple-vector
(if key
(mean-body vector simple-vector svref key)
(mean-body vector simple-vector svref nil)))
(vector
(if key
(mean-body vector vector aref key)
(mean-body vector vector aref nil)))
(otherwise
(if key
(mean-body vector sequence elt key)
(mean-body vector sequence elt nil)))))
This can be very useful in situations where we want to optimize code since it becomes easy to add the proper type declarations to the input arguments of an algorithm. Moreover, we keep the algorithm in a single place, making it easier to maintain. Depending on the situation, we can also define a function for each configuration. In the example we could have a mean-simple-vector and mean-vector.
I don't know if it has already a specific name but I like to call it the configurable algorithm pattern. I find it very useful. And thinking back to the reddit thread, what are your favorite macros design patterns? Which ones do you find useful and use them regularly? If you want to share, feel free to drop a line. I am interested in seing and learning other patterns!
Simple things in Lisp
This post is more a note to myself, so that I shouldn’t forget some interesting things, but perhaps trivial, that I came across this week. It ranges from those kind of things you sometimes bump into them and then forget, to the those you seldom use and suddenly find them cool. Instead of writing it in a post-it or a piece of paper (even if made of bits instead of paper), I’ve decided to write it here.
- SBCL available virtual address space at startup
- SBCL optimization notes: doing signed word to integer coercion
- Local macros
The experiments I do as part of my research work I run them in the cluster of my research group. This week, it was introduced some usage limits and one of them was precisely the available memory a job can use. So suddenly, all my jobs couldn’t run because they were trying to allocate 8GB (the new limit was 2GB). At first I found it strange since a single job when running didn’t consume more than 120MB. Well, thanks to my colleague Tiago and some googling, I’ve learned that SBCL in a 64bit system allocates 8GB although it might not use them. The solution to the problem turns out to be easy: start SBCL with the option -dynamic-space-usage with the amount of space you wish in megabytes. In my case, something below the 2GB limit. And then around 250 jobs were launched!
If there is anything I would really love to see/have is a good tutorial/manual of how to optimize Lisp code, specifically in SBCL. Recently I did some code optimization of a crucial function in my Ant System but is not easy. Although I was able to speed it up a lot in that function, some of the things I tried I ended up removing since it provoked some unstable behavior, i.e., it crashed. Moral of the story, it’s not easy to solve all the compiler notes about optimization. This week, while trying to optimize a bit a mandelbrot program, I came across the following:
; note: doing signed word to integer coercion (cost 20), for:
; the first argument of CHECK-FIXNUM
To me it was a little strange note since, in theory, everything was properly declared and was nothing fancy. In short, it was just an (incf x) with x declared as fixnum. Well, the note says it but only after some looking around I realized that by using (safety 1) the compiler will insert a type verification, hence the CHECK-FIXNUM in the note. Changing the safety level to 0, the verification is eliminated. Of course, you need to be sure that will always stay within the range of a fixnum, otherwise it will crash. One more little things that is good to be aware when optimizing Lisp code. I still wish more documentation about these optimization notes would exist.
I must confess that local macros is something I’ve barely used in my code. Yes, I know them. I’ve seen examples, etc, etc, but well, in actual programs of mine, I don’t remember using them. Again, this week, they simply decided to knock on my door. Macros are very useful for many things, one of them being to abstract boilerplate code. So, local macros are also cool for this. To keep it very simple, imagine that you have two methods, one specialized in lists and the other in arrays. Now, you want to access the list/array and do some complicated stuff. That stuff is the same except the part where you access the list and the array because for the list you use nth and for the array aref. The difference between them the arguments order: (nth index list) versus (aref array index). So, you can do this:
(defmethod do-stuff ((sequence array))
(macrolet ((access-position (position)
`(aref sequence ,position)))
(do-stuff-macro))))
(defmethod do-stuff ((sequence list))
(macrolet ((access-position (position)
`(nth ,position sequence)))
(do-stuff-macro))))
In short, you define in each method a local macro through macrolet, access-position, that is in turn used by the macro do-stuff-macro. In this simple way, you can develop the common body of the methods in a single place (the do-stuff-macro) and the different things are managed by the dispatch (the sequence type, list and array) and the local macros (the sequence access defined by macrolet). Simple but very cool!