David Nolen and I were recently discussing CLOS and method dispatch. I
implemented such a scheme for my own use. This post is an FYI for
David and anyone else who might be interested in generic functions and
predicate dispatch.
Here's a transcript of a sample session in which I exercise the
dispatch cases:
(def $gf (make-generic-function))
(add-gf-method $gf [nil] (fn [x] (str "found value: " (.toString x) "
of type: " (.toString (class x)))))
(add-gf-method $gf [java.lang.Number] (fn [x] (+ x 1)))
(add-gf-method $gf [java.lang.String] (fn [x] (str "Found string: "
x)))
(add-gf-method $gf [{:equals 5}] (fn [x] (str "Found 5!")))
(add-gf-method $gf [{:model {:name nil}}] (fn [x] (str "Found a thing
whose name is " (:name x))))
(add-gf-method $gf [java.lang.Number java.lang.Number] (fn [x y] (+ x
y)))
(add-gf-method $gf [java.lang.String java.lang.String] (fn [x y] (str
x " " y)))
(add-gf-method $gf [java.lang.String java.lang.Integer] (fn [s i]
(apply str (take i (cycle [s])))))
(add-gf-method $gf [{:test (fn [x] (= x "swordfish"))}] (fn [s] (str
"You said the secret word, you win a hundred dollars!")))
user> ($gf "hello" "world")
"hello world"
user> ($gf 2 3)
5
user> ($gf {:name "Barney"})
"Found a thing whose name is Barney"
user> ($gf 5)
"Found 5!"
user> ($gf "bananas")
"Found string: bananas"
user> ($gf 100)
101
user> ($gf 'some-symbol)
"found value: some-symbol of type: class clojure.lang.Symbol"
user> ($gf "foo" 5)
"foofoofoofoofoo"
user> ($gf "swordfish")
"You said the secret word, you win a hundred dollars!"
It works well enough to produce that transcript, but is very
preliminary. I'm sure there are bugs I haven't noticed yet, and no
doubt it's terribly slow. For one thing, I know it's doing a lot of
redundant comparisons at the moment.
It's missing a number of amenities and at least one important feature:
next-method is not yet implemented.
When called, a generic function matches the arguments it received
against formal parameters in its dispatch table. Matching of a formal
parameter works like so:
nil: match any value passed in this position
a Java class: match any value that is an instance of the class (or of
a subclass of it)
a Map of the form: {:model m}: match any value v for which (model? v
m) returns true
a Map of the form {:equals x}: match any value v for which (= v x)
returns true
a Map of the form {:test f}: match any value for which (f x) returns
true
Calling ($gf 2 3) or whatever works because the object in $gf is a
closure:
(defn make-generic-function [& [dispatch-table]]
(let [dt-ref (or dispatch-table (ref {}))]
(fn [& args] (apply-generic-function dt-ref args))))
There's a little trickery with sentinel values and metadata to enable
me to provide an API for adding and removing methods, which are stored
in the dispatch-table created in make-generic-function. It would be
nice to make that code a little less clever, but that would require a
convenient way to create a new kind of callable object; I'd need to be
able to define what happens when it's applied.
Now we'll see if this thing is useful enough to make it better.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---