I’ve been tinkering with Ikarus Scheme, an optimizing compiler for R6RS. I’ve ported Scheme48‘s elegantly simple implementation of generic functions to R6RS, and perverted it with some “improvements”: it now does zero allocation during a method call and it uses Dylan’s semantics for method selection. My goal is to get to predicate dispatching like JPred. Generic functions work by adding new methods to a global generic function object when the library is loaded. This worked because Scheme48 assumes that loading a library is just like loading it into the interpreter: all top-level expressions are evaluated. Unfortunately, R6RS does not specify when top-level expressions are evaluated, and Ikarus chooses to evaluate only when the library is first used. This means I have to explicitly call some function in each library (“init-static-expressions!”) to trigger evaluation. That sucks. On the other hand, my implementation sucks because I was only loading libraries for their side-effect on generics, while exporting nothing. It looks so weird it must be wrong.
This forced me to rethink how to implement generic functions. Now I realize that the standard implementation — a global singleton generic object that anyone can abuse — is not that great. I think Lisp’s defgeneric works this way, too. The problem is that you can’t temporarily add a method; for example, a new verbose printer within an exception handler. Or you may inadvertently remove someone else’s method when you add your own. Basically, a good rule of thumb is to avoid global variables and side-effecting code, and generics violate both rules.
There are a few ways to use generics. (1) In the usual implementation, you have a generic print function which you’d like to add new type-specific methods. This requires a side-effect on a generic object. (2) You’d like your own generic print function, which reuses and extends the old one without modifying it. For example, if I’m working with vectors of colors, I’d rather print them as RGB triples than as ints. But I only want to do that within my own code, not for all code. (3) You’d like an entirely new generic print, distinct from the existing one. Maybe the debugger needs a more verbose version of print. Given a good suite of combinators (aka MOP) for generics, I could do all three easily, though inefficiently. The hard part is designing the language that makes it as easy as saying “defmethod print (x <int>) … code …”, all without side-effects.