ifexpression binds the identifier “
it” to the value of the conditional in the scope of the consequent
(aif (find-broken-computer) (fix it))I have two objections to anaphoric macros. The first is that the binding of “
it” isn't obvious, the second is the inflexibility of the variable name. You are kind of stuck with “
it”. What if you wanted to use “
it” to mean something else? Maybe you have an IT department to fix your computers
(let ((it (find-department "IT"))) (aif (find-broken-computer) (tell it (fix it)))) ; name conflictor maybe you want to nest two anaphoric conditionals
(aif (find-broken-computer) (aif (find-broken-component it) (repair it) (replace it)))In this case, I want to replace the computer if I cannot repair the broken component, but the inner binding of “
it” shadows the outer binding and makes it inaccessible.
The solution is pretty obvious if you think about it (though sometimes it takes me a while to see the obvious). Just replace the conditional test form with a binding form
(aif (it (find-broken-computer)) (fix it))This makes it obvious we are binding the variable “
it” to the value returned by
(find-broken-computer), and it lets us choose the name we bind. If we want to nest these, it would look like this
(aif (computer (find-broken-computer)) (aif (component (find-broken-component computer)) (repair component) (replace computer)))But I'm not sure if this is so much more concise and clear than simply using a
letexpression that it is worth adding this syntax to the language. It's one more thing the reader of the code has to be prepared to encounter.
A slightly different approach would move the binding form closer to where it is used. Note that there is no point in binding a name in the alternative clause to the conditional because it will always have the value
(aif (find-broken-computer) (it (fix it)))and instead of using a
letbinding, I could use a
(aif (find-broken-computer) (λ (it) (fix it)))
aifno longer needs to be a macro but can be an ordinary function, which might be handy if your language doesn't have macros
(defun aif (it consequent &optional alternative) (if it (funcall consequent it) (if alternative (funcall alternative) nil))) (aif (find-broken-computer) (λ (computer) (aif (find-broken-component computer) (λ (component) (fix component)) (λ () (replace computer)))))The explicit lambdas make it obvious what is being bound and what the scope of the binding is, but they do add a bit of visual noise.
Instead of using anaphoric
if, I just write the slightly more verbose
(let ((computer (find-broken-computer))) (if computer (let ((component (find-broken-component))) (if component (repair component) (replace computer)))))The binding is obvious, and I get to choose the variable being bound; both problems solved. I don't see a compelling reason to use the anaphoric version.