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 -~----------~----~----~----~------~----~------~--~---