Lisp is known to be a highly dynamic language, where functions are first-class objects. It is possible to define and undefine functions on the fly as well as attach hooks to existing functions. These are in addition to the ability to pass functions as parameters to other functions and returning a function as the result of a function call.
This article will explore some of these ideas in detail.
Getting the Function Object from Symbol
Let us start with a basic example:
Our “bar” function takes two arguments and returns their sum. With this definition in place, we can get a handle to the function in many ways:
As you can infer, “#’bar” is actually a shortcut for the other two ways to get the function object.
How can we confirm that all three mean the same thing?
And here is another check:
What the above show is that given a symbol, it is possible to get the function definition associated with that symbol (provided the function is defined).
Changing the Function Definition Dynamically
Lisp allows us to change any function definition at runtime. In our example, we can easily change the definition of “bar” shown earlier:
It is that simple. Of course, the old definition is lost now.
Defining “before” and “after” Hooks
Changing the function definition at runtime is not common, although it is possible. However, many times, we might want to call a different function before and after the primary function is called. This can be useful in tracing the program flow, for example. How to do that?
Here is a naive implementation:
The set-function-hooks function uses a Hashtable to remember the original definition of the given a function and replace that definition with a function that applies the “before” and “after” functions appropriately while invoking the actual function. The remove-function-hooks function restores the original definition saved in the hashtable and removes the hashtable entry.
Here is an interaction showing how this works:
The reason why the given code is naive is because it allows only a single set of “before” and “after” functions to be attached. One can easily extend the logic to handle the general case.
Interestingly, Common Lisp already supports method combination, allowing us to attach “before“, “after” and “around” auxiliary methods to primary methods. You can go through my article on Aspect-Oriented Programming in Lisp to see how this is done. Note that this is applicable only for methods and not for regular functions.
Defadvice Feature
LispWorks has a much richer way of attaching advice, i.e. executable code, to a function. It is possible to execute multiple auxiliary code “before”, “after”, or “around” a function. This permits arbitrary action(s) to be carried out in the context of a given function, method or macro.
Here is an example session:
The primary “foo” function doubles the argument. The “around” advice calls the original function and then adds 100 to the returned result. That is why we get 300 as the result when we call (foo 100).
How to “undefine” a Function?
Common Lisp allows us to unbind (i.e. “undefine”) an existing function. Here is a sample session showing how this works:
That concludes our discussion on function hooks in Lisp. How you found the article useful. The sample code is available here.
Have a great weekend!
Recent Comments