Macros: Dangers and Alternatives

Macros allow us to write code that generates other code, making the Lisp languages a wonderful tool for metaprogramming and prototyping new language ideas. But, at some level, macros are just a sleight of hand: They let you trick the Lisp compiler/interpreter into accepting your own customized language constructs and treating them like standard Lisp. They are indeed a powerful tool in a programmer’s tool chest, but they are not as elegant as some of the other programming tools you’ve encountered in this book.

The main drawback of macros is that they can make it hard for other programmers to understand your code. After all, if you’re creating your own language dialect, other programmers won’t be familiar with it. Even your future self—say, in a year or two—may have a hard time understanding the structure of your code if you’ve made heavy use of macros. Because of this, experienced Lispers will do their best to use alternate techniques to macro programming whenever possible. Often, a beginning Lisper will write a macro in situations that could be addressed in other, cleaner ways.

For instance, it’s fun to see how we were able to clean up our my-length function by adding a couple of macros named split and recurse. However, in the previous two chapters, you learned about another tool, functional programming, which can also be used to clean up list-eater functions. One powerful function often used by functional programmers is reduce. It is a higher-order function that accepts a function and a list, and will call the function once for every value in the list. Here is the my-length function rewritten to use the powerful reduce function, rather than macros:

(defun my-length (lst)
   (reduce (lambda (x i)
             (1+ x))
           lst
           :initial-value 0))

As you can see, this new version of my-length easily blows away our previous version. It is shorter, and it doesn’t rely on any of the nonstandard macros that we created.

The first argument to reduce holds our reduction function . Its job is to keep track of, and update, an accumulated value, here named x. This variable x will hold the current accumulated value, which in this case will be the length of the list so far. This means we can simply add one to x to update it to its new value . Since the reduction function will be called once for every item in the list, it will, in the end, generate the length of the list. (The reduction function also receives, as an argument, the current item in the list, here given as the variable i. However, we do not need it for calculating the list’s length.) The next item passed to reduce is the list we want to reduce . Finally, since the accumulated length we’re calculating should have an initial value of zero, we indicate this by setting the :initial-value keyword argument to zero .

Clearly, there are other scenarios where the list-eater macros we’ve created in this chapter are still useful. There are many cases where the reduce function could not be so easily used. So in the end, there are still many situations where creating your own Lisp dialect is exactly the right solution to a problem, as you’ll see in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.44.229