From: Avi Bryant Date: 2001-10-29T04:27:08+09:00 Subject: [ruby-talk:23689] CLOS and macros (long - was Re: Ruby macros) On Sun, 28 Oct 2001, Robert Feldt wrote: > Could you give some short examples showing the power of the CL macro > system? > Likewise for CLOS, could you give a short intro to what it is? My > background is imperative programming languages and then pure functional > ones but I've never used Lisp. Classes in CLOS are basically just structs with multiple inheritance. Polymorphism is achieved through generic functions - these are functions which can have any number of methods (functions with type signatures) attached to them. When the generic function is called, it dispatches to the most specific method, taking all of its argument types into account. call-next-method is provided to dispatch to the next-most-specific method. Methods can also be declared as :before, :after, or :around, which compose onto rather than ovverriding existing methods. Other types of method combination are also possible - for example, a generic function can be declared to return the result of summing up the return values of all of the methods that apply, whatever the specificity. The interesting thing is that this is all achieved with macros that expand to straight procedural function calls. For example, the method declaration (defmethod add ((a Foo) (b Bar)) (+ a b)) would expand to a number of calls creating a method object, installing that method object into the appropiate generic function (and creating a generic function for "add" if one didn't already exist), attaching the closure (lambda (a b) (+ a b)) to it, probably adding the (Foo Bar) type signature to some dispatch table somewhere, etc. A generic function expands to a regular function that does dispatch to the appropiate method. Similarly, the class definition (defclass Foo (AbstractFoo) ((name :read get-name))) will create all the necessary structures for the Foo class, generate the get-name accessor, etc. People often create specialized versions of these macros - the "IMHO" web framework, for example, has a macro "defpage" that accepts some options specialized for web development. CLOS also includes a sophisticated metaobject protocol, which allows essentially anything in the system to be overridden - including the expansion of those macros, accesses to instance variables, how method dispatch works, etc. This is covered in great detail in Kiczales et. al "The Art of the Metaobject Protocol". That's a very complex use of macros; a simpler example might be Paul Graham's "anaphoric and", which looks like (aand (foo x) (bar it) (zap it) (quux it)). This expands to code that in ruby would look like (a = x.foo) && (b = a.bar) && (c = b.zap) && (d = c.quux) Ie, it chains the results until it hits a nil, at which point it shortcuts out. A classic example from scheme is "let" - again in pseudo ruby, this would transform let a = 1, b = 2 in a * b end into (proc{|a,b| a*b}).call(1,2) except that procs don't introduce a new scope in ruby so that doesn't work right ;-). The thing to realize about why macros work so well in lisp, though, is that the parenthesized syntax is roughly like writing ruby code in the form [:if, [:<, :x, 1], [:+, :x, 2]], that is, it's treated as nested lists of symbols and literals, which makes macro expansion functions much easier to write. However, Dylan manages to have a comparable macro system (well, it's actually much more like Scheme's than like CL's) with an infix syntax. An example of defining a macro in Dylan: define macro with-mutex { with-mutex (?mutex:expression) ?code:body end } => { let mutex = ?mutex; check-error(pthread-mutex-lock(mutex)); block () ?code cleanup check-error(pthread-mutex-unlock(mutex)); end block; } end with-mutex; The code before the => is a pattern, the code after is its expansion, and the variables that start with ? are replaced in the expansion with code from the pattern. > The upcoming Smallscript execution engine AOS (see > www.smallscript.org) support multimethods and if there'll be a Ruby > front-end to it multi-methods are a real possibility. At least as an > experimental feature. > > Regards, > > Robert > >