This is nothing short of amazing for those who want to build ad-hoc type systems. (defstruct #^{:type ::my-struct} my-struct :a :b) (type #'my-struct) ; -> :user/my-struct!
I have a lot of code fixing to do now :) Also Konrad's point that using multimethods for dispatch-fns would be ugly, but actually I think this actually works out well! (defmulti multi-dispatch type) (defmethod multi-dispatch ::my-type [x] (:tag x)) (defmethod multi-dispatch ::another-type [x] (:protocol x)) (defmulti my-multifn multi-dispatch) (defmethod my-multifn ::some-tag [x] (print "An object!")) (defmethod my-multifn ::some-protocol [x] (print "A protocol")) (my-multifn #^{:type ::my-type} {:tag ::some-tag}) ; -> "An object!" (my-multifn #^{:type ::another-type} {:protocol ::some-protocol}) ; -> "A protocol!" Amazing. On Wed, Feb 25, 2009 at 7:51 PM, Rich Hickey <richhic...@gmail.com> wrote: > > > > On Feb 25, 12:19 pm, Konrad Hinsen <konrad.hin...@laposte.net> wrote: > > On Feb 25, 2009, at 17:40, David Nolen wrote: > > > > > Should print and println be multimethods? It seems the ability to > > > custom print a data structure would be generally useful. > > > > It's already there, just not documented as far as I know. All the > > printing routines end up calling clojure.core/print-method and > > clojure.core/print-dup, which are multimethods dispatching on class. > > > > > Not totally following here, looking at your code... Pardon my > > > slowness but why not dispatch on :tag first, and class if no :tag? > > > > That's possible, of course, but the result is a multimethod that > > works for (a) class-based types and (b) one specific type hierarchy > > hard-wired into the selector, such as your :tag. If I write some > > other type system, it won't fit in. That makes it impossible to > > define truly general interfaces that are open for anyone to implement. > > > > Take print-method in clojure.core as an example: How would you > > implement its selector, such that anyone could plug in their type > > system based on some tagging scheme? Perhaps one could come up with a > > scheme in which the selector function is itself a multimethod, but > > that would probably be quite messy. > > > > > Tags of course should be namespaced so is this really that much of > > > an issue in practice? Since tags are keywords they don't have the > > > interning conflict problem that symbols of the same name generally > > > do, right? > > > > Right. Namespaced tags go a long way to avoid name clashes. However, > > they are still not the private property of any one library module. > > Your namespaced tag is an object that can end up in any data > > structure, intentionally or by error. Only time will tell if this is > > a problem in practice. > > > > Anyway, my two main arguments in this issue are printing and open-to- > > anyone interfaces with multimethods. These are the problems that I > > had in my own code and that I wanted to solve. Printing is important > > for complex nested data structures and interactive use. Generic > > interfaces are a very nice feature to have. I came across this > > problem twice: > > > > 1) In clojure.contrib.accumulators. Ideally, it should be possible to > > implement additional accumulator types elsewhere, using the same > > interface. With the current implementation based on meta-data, that > > is possible, but only for data types that allow meta-data. It is thus > > not possible to build accumulators based on Java classes, at least > > not without wrapping them in a map or vector. Moreover, the selector > > function, based on meta-data with a fallback to classes, is neither > > elegant nor efficient, and much of the code in the library deals with > > the meta-data tagging scheme rather than with its real job. > > > > 2) In clojure.contrib.streams-utils. Again, I wanted to define a > > generic interface to data streams. At the moment, it uses a > > multimethod dispatching on class, but that doesn't leave much room to > > define different data stream sources. That was in fact what prompted > > me to write the types library. > > > > You raise interesting issues and I'd like to explore them further. I'm > not sure the issues you have with type-tag-or-class dispatch are all > that prohibitive. In any case, I've added a type function that returns > the :type metadata or the class if none: > > user=> (type #^{:type ::Fred} [1 2 3]) > :user/Fred > > user=> (type "foo") > java.lang.String > > which should help people standardize. > > If you want to multiplex multimethods for your ADT type, you can just > define a single :type ::ADT, and then sub-dispatch on e.g. :adt-type. > > I'm not sure the mechanism you are using (actual Class types) allows > for any more overloading - Class is a single slot too, after all. > Interfaces offer some mixin potential, but they can't be superimposed > on existing classes. The same mixin capabilities of interfaces are > available for type tags, just say; > > (derive ::my-type :freds/interface) > (derive ::my-type :ethels/interface) > > etc. > > The trick here is that you *can* superimpose such tags on existing > classes: > > (derive String :freds/interface) > > A bigger problem with the mechanism you've chosen is its dependence on > some implementation details. I haven't promised, e.g. that every fn > generates a unique class type. > > Also, there may be issues on re-evaluation - it's not necessarily > going to yield the same classes. > > Rich > > > > --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---