Tuesday, January 4, 2011

EQL Specializers

Pascal Costanza asked an interesting question: Why are EQL specializers convenient for smooth integration with object instantiation?

My original answer was simply that when you want to customize make-instance for a particular class of objects that you end up writing methods like this:
  (defmethod make-instance ((class (eql (find-class 'foo))) ...
This is doable without EQL specializers, but it would be kludgy.

While thinking about this some more, though, it occurred to me that EQL specializers are probably a necessary element for reflective programming.

When you reflectively program the object system, you operate at a meta-level where abstractions such as classes are reified as concrete instances of a meta-class. At the reflected level, object instances are no longer the data being manipulated. Instead, object classes are used to represent sets of instances, and the classes themselves are represented by instances of meta-class objects.

When you cross abstraction levels in this way, you need a set of level-shifting primitives. One of the most important of these is quotation. Quotation is level-shifting primitive that lifts (injects) an instance from beneath the abstraction level to the level itself.

In CLOS, EQL specializers are the quotation mechanism for method dispatch.

7 comments:

John Cowan said...

I admit these specializers are convenient, but I think they're unprincipled. If you want to match on Foo class itself, make it an instance of the FooMeta metaclass rather than the default metaclass. (Indeed, I don't know of any other use of metaclasses in CL.)

Similarly, if you want to match on a non-class singleton, have a class of which that singleton is the sole member. I admit this is more awkward, as random singletons aren't typically CLOS objects, but that's a limitation of the CL type system.

Jrm said...

You can certainly use EQL specifiers in an unprincipled way. I've seen the factorial function written such that the base case is handled by an EQL specializer on the number zero.

I don't think you can completely sidestep EQL specializers by creating class singleton metaclasses, though. (Leave aside the non-class singletons.) CLOS is built upon several mutually recursive protocols. Some of these protocols are level-crossing (that is, they work at the meta-level to effect changes to the level). There would be infinite meta-recursion if you required that every specialization operated on the level above.

In other words, by making the Foo class be an instance of the FooMeta metaclass, you have only pushed the problem back by one level. If you need to specialize on the FooMeta metaclass, then it would need to be a instance of the FooMetaMeta meta-metaclass, which would be an instance of the FooMetaMetaMeta meta-meta-metaclass and so forth. EQL specializers break this circularity.

Nick Barnes said...

Surely this isn't going to work if anyone ever subclasses FOO? Messing about with programmer expectations like this is dangerous.

Jrm said...

Nick Barnes said: Surely this isn't going to work if anyone ever subclasses FOO? Messing about with programmer expectations like this is dangerous.

Definitely true. In general, you probably don't need EQL specializers unless you are
cross-level meta-programming, discriminating on a tagged union (where the tag is essentially an ENUM and couldn't be logically subclassed), or have a domain in which you have explicitly added individual elements (much like a lifted domain).

John Cowan said...

True that you might need such recursion on meta, but you only need it to the extent that you actually use it. And that's always possible, because any standard class (including a metaclass) can have any descendant of standard-class as its metaclass. So in particular you need to make FooMeta be an instance of FooMetaMeta only if you are actually going to specialize on FooMeta at some point. Otherwise it can just be an instance of standard-class.

Jrm said...

John Cowan said... True that you might need such recursion on meta, but you only need it to the extent that you actually use it. And that's always possible...

I'm not sure it is always possible because you may not have access to the class definition. If you didn't write the class definition, then you cannot (easily) insert an appropriate meta-class after the fact.

John Cowan said...

That also is just a limitation of the CL class system.