How to generate functions with macros
How to generate functions with macros
This blog post will show you how to use macros to write functions with similar behavior. This general method will greatly reduce the amount of code that you need to type and will keep your codebase clean and easy to test.
Let’s assume for a moment that you are building a system where you have users.
A user has several fields including name
, email
, phone-number
and
gender
. For the purposes of this article we will model a user as a simple map.
The user data structure.
Requirements
At some point in time, your boss comes into your office and says: “Jim, we need to be able to find users in the database by name, email, phone number and gender and we need it now!”
You are an awesome clojure developer so you quickly come up with a straight forward solution:
At this point you realize that you are repeating yourself and since you are an awesome developer you start to think how you can do this better.
Keeping it DRY with functions
You immediately realize: well what if I had a function find-by-field
which
finds a user in the database given a field and a value for that field.
You look at your work and think, damn, that’s a good simple, clean solution.
But the question remains, can we do better? Yes we can! Looking at your code
you quickly realize that the find-by-{field}
functions can be re-writter
with macros which will remove all the needless boiler plate and code repetition.
It’s macro time!
As always when describing macros I like to first see how the macro invocation will look like as well as the expanded code and then it’s usually easier to implement the macro.
The cool think about this approach is that it actually defines good ol’ functions which can be composed and used as you would normally use functions anywhere else in your awesome clojure codebase.
So without further delay, here is the code: A 20-liner can be used to define as many finder functions as possible.
Wrapping it up
So there you go, a simple extensible approach with 0 performance degradation that will make your code clean and extremely easy to extend. The key points to note about the macro approach:
- Adding another finder function is as easy as adding another argument to the
defuser-finders
macro invocation - The generated code is as performant as hand written code. Since macro expansion happens before runtime, these functions will actuallty be compiled into bytecode.
- The generated code comes with documentation so users of your code can
(doc find-by-user)
.
I hope you liked this post. As always critticism and comments are more than welcome. Cheers and happy coding!