One feature of CLOS which I really miss in Scala is method qualifiers.
I wonder what the best approach to working around this missing feature is.
Here is an example.
As an API, I have a method named
foo. All users of my library make calls to
foo is well documented in
.md files, and source files, and in other documentation inside and outside the project file hierarchy. The method is defined on all classes in a class hierarchy, and the method takes exactly one argument which is of the same type as the top of the class hierarchy.
This means if I have
n leaf classes, there are possibly
n^2 cases to consider. Usually several cases are considered together, but the unit testing framework needs to make sure that all
n^2 cases are thoroughly tested.
Suppose the top class is
A and the leaf classes are
Z, perhaps with some intervening abstract classes.
V.foo(a) is the same algorithm independent of the class of its argument, that code can go into the method defined in the
V class—no problem.
But what about if
a.foo(W) is the same algorithm for all leaf classes. Where should that code go? Moreover, what if it is algorithmically better or even algorithmically necessary that whenever the argument has class
W, then the other code needs to be circumvented. How do make sure that the check for
W is made first, regardless of the class of the object on the left-hand-side of the dot?
There are two ugly ways that I can think of to implement this in Scala.
Write a function,
bar(perhaps a method in A) and call it atop every
foomethod, including program logic such as
if a.bar(b).nonEmpty return its-value else .... Remember to add a comment telling the programmer, if you implement a new
foomethod for a new class, remember to call
baron the first line of the method including logic to deal with its return value. This has the horrible disadvantage that if
barwill be called twice.
fooDown, but don’t rename the call-sites. Now re-implement
fooas a call to
barfollowed by a call to
fooDown. Now re-read all the documentation and selectively replace
fooDown. So the API becomes, call
In CLOS there is a concise feature to handle this. It is called an
around method. Any code which needs to happen as a precondition of calling the method goes in the around method. and when
call-next-method (the equivalent of
super.foo()) is called, that pre-condition code DOES NOT get re-evaluated.