More Literate Programming: Language-Level Anaphora
Last month I blogged about Literate Programming with jMock, and also about using anaphora to avoid repetition in the tests. (An anaphor is a word like it that refers to something previously referred to.)
This got me thinking: is it possible to use anaphora more widely at the language level? Would such constructs be useful? Before trying to do this in Java I looked at more dynamic languages, starting with a very quick look at Lisp, where I first came across anaphora in programming languages.
Common Lisp
Paul Graham, in his wonderful book ANSI Common Lisp (which is well worth a read, by the way, even if you never intend to write a line of Lisp), introduces an anaphoric if that captures the test condition in a variable called it. His example is:
(aif (calculate-something)
(1+ it)
0)
This can be read as "if the variable calculate-something is non-zero, then return one plus its value, otherwise return zero". The aif construct is actually a macro, a very powerful feature that effectively allows you to re-write the language. I won't go into the details here as it's very clearly explained in the book. In a nutshell, the Lisp compiler transforms the expression into another chunk of code (defined by the macro) that it can understand - in this case a regular if with a bit of variable capture.
Let's try another language - Ruby.
Ruby
Here's how aif would be used in Ruby:
hash = { 'a' => 'peach', 'b' => 'pear', 'c' => 'plum'}
aif hash['a'] do
puts @it
end
This code snippet prints peach to the console
Ruby doesn't have macros, so the way this is implemented is not like Lisp, it's actually closer to the jMock approach. We basically extend Object, and add an aif method that takes a conditional expression and a block. If the expression is true, then the instance variable @it (instance variables always start with @ in Ruby) is set to the value of the expression and the block is executed. Here's the definition:
class Object
def aif(expression)
if expression
@it = expression
yield
end
end
end
The Ruby implementation is inferior to the Lisp one in a few ways (although some of this could be down to my inexperience in Ruby - I invite seasoned Rubyists to improve the code!). Firstly, there is no way that I can see of supporting an else clause for aif (the Lisp version does this easily). Secondly, there are concurrency issues - storing state in an instance variable is dangerous if multiple threads are using the object. However, this is fairly straightforward to solve with synchronization or by using thread-local variables. Thirdly, there is a minor, but potentially irritating, syntactic difference between aif and if: aif takes a block and hence has an extra do. Compare:
if hash['a']
puts hash['a']
end
and
aif hash['a'] <b>do</b>
puts @it
end
Java
Can you do the same thing in Java? Let's try to replicate the Ruby approach since Java doesn't have Lisp macros. We can't add methods to Object in the way we can in Ruby, so instead create a class called AnaphoricObject with a static method called aif. We can then either extend AnaphoricObject or statically import aif when we want to use anaphoric if.
Now we hit a problem: what does the definition of aif look like? It would take an Object as the test expression, but then we would need to cast the it variable to the type we wanted. Doable, but not very pleasant. (Aren't we trying to improve readability?) Worse, how do we supply a block of code? We can't do it. There is probably a way to do it using anonymous inner classes, but I don't want to go down that alley - we'll lose all improvements in syntax which is why we were trying to do this in the first place.
Conclusion
Obviously, I like Java a lot, but I know its limits. Anaphora work well for an API like jMock, but not at the Java language level. Even Ruby, touted for its ease of meta-programming, struggles to provide a nice implementation of anaphoric if (although, again, I'd be happy to be proved wrong on this). Lisp manages it, if only because it has macro support. (It's probably possible in your favourite language too.)
But, do we really need anaphoric if - after all, you can probably argue that you've never needed it. This is actually a weak argument. For example, I use Java 5 static imports all the time now and wouldn't like to give them up, but I didn't rage about not having them before I was introduced to them. I didn't know what I was missing. Similarly, once you've used constructs like anaphoric if, you get to know when it is useful, then start missing it in languages where it's not available. Here's Paul Graham again (from ANSI Common Lisp):
Is it worth writing a macro just to save typing? Very much so. Saving typing is what programming languages are all about; the purpose of the compiler is to save you from typing your programs in machine language. And macros allow you to bring to your specific applications the same kinds of advantages that high-level languages bring to programming in general. By the careful use of macros, you may be able to make your programs significantly shorter than they would be otherwise, and proportionately easier to read, write, and maintain.
Although he's talking about macros, I think the point is more general: it's worth striving for higher-level abstractions to make our programs more literate.
- Login or register to post comments
- Printer-friendly version
- tomwhite's blog
- 1266 reads






Comments
"which is well worth a read,
by Anonymous - 2010-10-13 10:07
"which is well worth a read, by the way, even if you never intend to write a line of Lisp" I regularly hear this said about Lisp books. I've never heard this said about a PHP book, or a Visual Basic book, or even a C++ book.