Note, if you’re a macro guru, you can ignore this post, this is just used to record my learning path to master clojure macro.
Many times, I find myself using
format to generate string from string template, and values filled in are always derived from local variables, so I’m wondering if I can use macro to generate this kind of code for me.
So I wrote the first version:
with this macro I can use
(format "%s://%s.%s/foo/bar/%s" scheme subdomain host token)
Yes, this doesn’t eliminate much typing, but I don’t need to check if the number of “%s” matches the number of args, and template looks more pretty. Anyway I happily used it in some places, but after some days, I realized the code generated from it is not optimal: it generate a reduce call with string replace in reducer fn, and this is runtime cost, not compile time cost, which means it will call reduce to generate wanted string from template at runtime!
I want generated code to be more efficient, to do this, I have to move some computation from runtime to compile time, and generate something like
(str scheme "://" subdomain "." host "/foo/bar/" token)
this will be much better.
To achieve this, I need one util function that partition string with regular expression, it will return matched string and unmatched string sequentially, instead of just return matched like
re-seq do. After some searches, I found it doesn’t exist, so I have to implement it on my own, and finally finished optimized version:
re-partition is indeed a little ugly, but it involves java object, and have to handle some corner cases, this is the best I can do.
The problem with this macro is the argument must be string literal, variable that have string value won’t works. We can get around this by probing its argument type, and generate optimal code on string or code that doing re-partition at runtime on other types, but since I don’t need that ability, I didn’t do that, maybe you can have a try.
Best thing about macro: you can use Lisp function at both runtime and compile-time. I’m very appreciate its homoiconicity now.