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 clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to