Duncan Rose’s work from 10 years ago continues to guide my KLOS effort. One of the things he mentioned in this post is the following:
The syntax is probably 95% what you’d expect from reading the Dylan docs, assuming a cursory understanding of Dylan syntax and a similar understanding of CL syntax. A few of the macros are slightly different (for example, many of the CL macros use keywords for “literals” in macros (such as :PANE in frame-defining forms - the Dylan uses just PANE), and CLIM’s MAKE-PANE has returned since CL doesn’t implement dispatching the same way Dylan does (Dylan’s method is superior in my humble opinion). Also the Dylan “xxx-setter” methods are replaced with CL “(setf xxx)” methods).
I’ve documented how to deal with keywords already, and that work continues, but his point about MAKE
was something that I wanted to know more about. I found a thread on comp.lang.lisp
from 2006 this afternoon and it elaborates on his comment about MAKE-PANE
.
In his first post, he wrote:
Using EQL specializers works, […] but is insufficient for my needs since eql specializers don’t form any kind of superclass / subclass hierarchy (i.e. I’d need an eql specialized MAKE-INSTANCE for each abstract class in the class hierarchy, which I’d rather avoid…).
but looking at the Deuce source code, I think there is a specialized MAKE for each of the abstract class:
define sealed inline method make
(class == <deuce-pane>, #rest initargs, #key, #all-keys)
=> (pane :: <simple-deuce-pane>)
apply(make, <simple-deuce-pane>, initargs)
end method make;
Either way, maybe I’ll understand more once I have running code. It also brings to mind that singleton specialization is a must-have in KLOS.
Reading some more, I think I found the feature that’s unique to Dylan (and probably not in Common Lisp).
Here’s a snippet from ‘gadget-mixins.dylan’:
//--- If you change this method, change the one in sheets/frame-managers
define method make
(pane-class :: subclass(<gadget>),
#rest pane-options,
#key port, frame-manager: framem, #all-keys)
=> (pane :: <gadget>)
dynamic-extent(pane-options);
let framem = framem
| *current-frame-manager*
| port-default-frame-manager(port | default-port())
| error("Can't find a frame manager to use with 'make-pane'");
let (concrete-class, concrete-options)
= apply(class-for-make-pane, framem, pane-class, pane-options);
// If there's a mapping from the abstract pane class to a concrete pane
// class, then use it. Otherwise just try to create a class named by the
// abstract pane class.
if (concrete-class == pane-class)
apply(next-method, pane-class,
frame-manager: framem, pane-options)
else
//---*** Unfortunately, this recursive call to make will call
//---*** 'class-for-make-pane' again. How to speed this up?
apply(make, concrete-class,
frame-manager: framem,
concrete-options | pane-options)
end
end method make;
The key thing is the specialization on pane-class :: subclass(<gadget>)
. This subclass specialization is spelled out here in DEP 5
With that available, here’s the code for creating buttons:
define function button-selection-mode-class
(selection-mode :: <selection-mode>) => (class :: <class>)
select (selection-mode)
#"none" => <push-button>;
#"single" => <radio-button>;
#"multiple" => <check-button>;
end
end function button-selection-mode-class;
define sealed inline method make
(class == <button>, #rest initargs,
#key selection-mode :: <selection-mode> = #"none", #all-keys)
=> (button :: <button>)
apply(make, button-selection-mode-class(selection-mode), initargs)
end method make;
// Inside of things like menus, we want to be able to get the value of
// a button. When the button is inside some kind of a button box, its
// value is the value of the client box.
define method button-gadget-value (button :: <button>) => (value)
select (gadget-selection-mode(button))
#"none" =>
gadget-value(button);
#"single", #"multiple" =>
let client = gadget-client(button);
when (client)
gadget-value(client)
end
end
end method button-gadget-value;
This code is pretty neat - it does what Duncan Rose talks about here, which is how <button>
returns a more specific implementation, and working with the Frame Manager, it’ll also return an implementation-specific class (<gtk-button>
or <win32-button>
, for example).