Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On 31.03.2009, at 21:48, Konrad Hinsen wrote: On 31.03.2009, at 18:50, Mark Engelberg wrote: On Tue, Mar 31, 2009 at 9:45 AM, Konrad Hinsen konrad.hin...@laposte.net wrote: I think this should be sufficient to cover all cases you mentioned, but of course it needs to be tried in practice. I think your idea of specifying a sequence of items to try in the dispatching function, at the point of definition for the multimethod, violates the principle of allowing library consumers to easily extend this on their own without having to contact the library designer. Maybe I wasn't clear about one point: the return sequence is not the sequence of concrete implementations to try (the dispatch function wouldn't even necessarily know them), but a sequence of starting points in the hierarchy from which the standard lookup would start. One night's sleep later, I see that this is not sufficient. The dispatch function is defined before the multimethod, and thus potentially inside some library, which cannot know all the needs of its potential clients. So yes, there has to be a way to specify preferences later on. One possibility would be to make the dispatch function itself a multimethod, or have it call one, but I am not sure that's a good solution. Konrad. --~--~-~--~~~---~--~~ 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 -~--~~~~--~~--~--~---
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On Apr 1, 2009, at 14:47, Rich Hickey wrote: I've added get-method (SVN 1338). Great, thanks! Note how you can name methods for diagnostic purposes. This doesn't introduce names into the namespace, just puts a name on the fn object. It's also useful for recursive calls, if you are sure you want to stay inside the method and not do a new dispatch. Konrad. --~--~-~--~~~---~--~~ 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 -~--~~~~--~~--~--~---
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
Very cool. On Wed, Apr 1, 2009 at 8:47 AM, Rich Hickey richhic...@gmail.com wrote: I've added get-method (SVN 1338). (derive ::Circle ::Shape) (derive ::Rect ::Shape) (defmulti area :Shape) ;note - you can name methods (defmethod area ::Shape area-shape [x] nil) (get-method area ::Rect) #user$area_shape__43 user$area_shape_...@674a93a6 (defmethod area ::Rect area-rect [r] (* (:wd r) (:ht r))) (defmethod area ::Circle area-circ [c] (* (. Math PI) (* (:radius c) (:radius c (get-method area ::Rect) #user$area_rect__18 user$area_rect_...@4e42751f (get-method area ::Circ) ;not there nil (get-method area ::Circle) #user$area_circ__20 user$area_circ_...@74da0c91 ;if you don't think squares are rectangles, you can still reuse implementation (silly example) (defmethod area ::Square area-square [sqr] ((get-method area ::Rect) {:wd (:dim sqr) :ht (:dim sqr)})) (area {:Shape ::Square :dim 20}) 400 Note how you can name methods for diagnostic purposes. This doesn't introduce names into the namespace, just puts a name on the fn object. 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 -~--~~~~--~~--~--~---
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On Mar 31, 2009, at 16:32, Rich Hickey wrote: Here are some problems/limitations of existing OO/GF systems that I don't intend to repeat: ... I agree that these are not desirable features. I have had to work around some of them many times in the past. Traditional OO combines aspects that should better be handled separately. Here are the areas I'm looking to improve: - There's no easy way to talk about the method you would get if you were dispatch value X. Note that this is not the same as call-next- method, which reintroduces global ordering requirements, but allows for easy explicit reuse of already-defined methods. That would be VERY nice to have. More than once I ended up writing a private function that I then called from several methods in the same multimethod, to avoid code duplication. - Currently, a preference doesn't encompass the path through the preferred value to its ancestors, but could. - If you have a set of common preferences, there's no easy way to create them in advance and share them among methods. There are issues here related to ensuring preference consistency and caching. At the moment, a multimethod takes two dispatch-oriented parameters: a hierarchy and a dispatch function that returns a single item from the hierarchy. How about generalizing the dispatch function in two ways: - it can return a sequence of items that will be tried in order - it can access the hierarchy Accessing the hierarchy is a minor point since this is already possible (write a dispatch function that uses parents, ancestors, etc.; for a hierarchy other than the global one, make the dispatch function a closure that refers to the hierarchy). Maybe it could be made more convenient. Returning a sequence should be sufficient to let the dispatch function handle any kind of preference. prefer-method could thus go away. Instead, there would be a set of utility functions to facilitate writing dispatch functions for common cases. A single dispatch function could be used for any number of multimethods. I think this should be sufficient to cover all cases you mentioned, but of course it needs to be tried in practice. Konrad. --~--~-~--~~~---~--~~ 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 -~--~~~~--~~--~--~---
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On Tue, Mar 31, 2009 at 9:45 AM, Konrad Hinsen konrad.hin...@laposte.net wrote: I think this should be sufficient to cover all cases you mentioned, but of course it needs to be tried in practice. I think your idea of specifying a sequence of items to try in the dispatching function, at the point of definition for the multimethod, violates the principle of allowing library consumers to easily extend this on their own without having to contact the library designer. --~--~-~--~~~---~--~~ 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 -~--~~~~--~~--~--~---
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On Mar 31, 11:45 am, Konrad Hinsen konrad.hin...@laposte.net wrote: On Mar 31, 2009, at 16:32, Rich Hickey wrote: Here are some problems/limitations of existing OO/GF systems that I don't intend to repeat: ... I agree that these are not desirable features. I have had to work around some of them many times in the past. Traditional OO combines aspects that should better be handled separately. Here are the areas I'm looking to improve: - There's no easy way to talk about the method you would get if you were dispatch value X. Note that this is not the same as call-next- method, which reintroduces global ordering requirements, but allows for easy explicit reuse of already-defined methods. That would be VERY nice to have. More than once I ended up writing a private function that I then called from several methods in the same multimethod, to avoid code duplication. - Currently, a preference doesn't encompass the path through the preferred value to its ancestors, but could. - If you have a set of common preferences, there's no easy way to create them in advance and share them among methods. There are issues here related to ensuring preference consistency and caching. At the moment, a multimethod takes two dispatch-oriented parameters: a hierarchy and a dispatch function that returns a single item from the hierarchy. How about generalizing the dispatch function in two ways: - it can return a sequence of items that will be tried in order - it can access the hierarchy Accessing the hierarchy is a minor point since this is already possible (write a dispatch function that uses parents, ancestors, etc.; for a hierarchy other than the global one, make the dispatch function a closure that refers to the hierarchy). Maybe it could be made more convenient. Returning a sequence should be sufficient to let the dispatch function handle any kind of preference. prefer-method could thus go away. Instead, there would be a set of utility functions to facilitate writing dispatch functions for common cases. A single dispatch function could be used for any number of multimethods. I think this should be sufficient to cover all cases you mentioned, but of course it needs to be tried in practice. I think the orthogonal elements of multifunction dispatch are type computations, traversals, and matchers (and the functions being dispatched to, of course). Dispatch function is maybe an unfortunate name; those functions aren't dispatchers, they're type-returning functions--functions that compute selector values. If you have a way to compute selector values from inputs and a way to specify the order in which matches against them will be tried, and a way to specify how matching is to be performed, then you have the tools to specify any dispatching algorithm you like. Clojure presently provides some tools for this: dispatch functions are type-returning functions; hierarchies are graphs of selector values; isa? and prefer- method are a control language for describing traversal, and isa? is also a matching function. But these are restricted versions: dispatch functions are meant to return a restricted range of values--symbols, keywords, classes, and certain collections of those values; multifunction dispatch supports hierarchies, but not other kinds of graphs; isa? and prefer-method are the only vocabulary we have for describing traversal, and isa? is the only matcher (almost the first thing I wanted to do when using MultiFns was replace isa? with my own matching function). The tools can be more expressive. Multifunction dispatch can be built on user-specified traversals of user-designed graphs of selector values, computed by user-supplied type-returning functions, and using user-specified matchers. The traversal order, the range of selector values, the language for describing traversal, and the matching procedure can all be more first-class and more orthogonal. Special cases can be provided for specific combinations known to be especially useful or efficient. With a full set of tools for describing dispatch, any differences of individual taste in function dispatching and type relationships becomes moot, because the tools are there to implement whatever type relationships and dispatching algorithm you want. Rich doesn't want to enshrine a particular traversal order in Clojure; I want the power to comprehensively specify traversal order in advance for certain uses; very well, any traversal order (or no traversal order) may be defined, as you like. He wants function dispatching and data definitions to be separated and so do I; very well, we have the freedom to compute whatever values we wish for use as types, and to arrange them in whatever sorts of graphs we like. Everyone wants things to be efficient, and likely people also want common use cases to be provided ready-to-wear; very well, those cases can be provided as built-ins or elements of libraries, with special treatment in
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On Mar 31, 2:18 pm, mikel mev...@mac.com wrote: On Mar 31, 11:45 am, Konrad Hinsen konrad.hin...@laposte.net wrote: On Mar 31, 2009, at 16:32, Rich Hickey wrote: Here are some problems/limitations of existing OO/GF systems that I don't intend to repeat: ... I agree that these are not desirable features. I have had to work around some of them many times in the past. Traditional OO combines aspects that should better be handled separately. Here are the areas I'm looking to improve: - There's no easy way to talk about the method you would get if you were dispatch value X. Note that this is not the same as call-next- method, which reintroduces global ordering requirements, but allows for easy explicit reuse of already-defined methods. That would be VERY nice to have. More than once I ended up writing a private function that I then called from several methods in the same multimethod, to avoid code duplication. - Currently, a preference doesn't encompass the path through the preferred value to its ancestors, but could. - If you have a set of common preferences, there's no easy way to create them in advance and share them among methods. There are issues here related to ensuring preference consistency and caching. At the moment, a multimethod takes two dispatch-oriented parameters: a hierarchy and a dispatch function that returns a single item from the hierarchy. How about generalizing the dispatch function in two ways: - it can return a sequence of items that will be tried in order - it can access the hierarchy Accessing the hierarchy is a minor point since this is already possible (write a dispatch function that uses parents, ancestors, etc.; for a hierarchy other than the global one, make the dispatch function a closure that refers to the hierarchy). Maybe it could be made more convenient. Returning a sequence should be sufficient to let the dispatch function handle any kind of preference. prefer-method could thus go away. Instead, there would be a set of utility functions to facilitate writing dispatch functions for common cases. A single dispatch function could be used for any number of multimethods. I think this should be sufficient to cover all cases you mentioned, but of course it needs to be tried in practice. I think the orthogonal elements of multifunction dispatch are type computations, traversals, and matchers (and the functions being dispatched to, of course). What about predicate, or rule based dispatch - why hardwire the notion of types, traversal or matching? Dispatch function is maybe an unfortunate name; those functions aren't dispatchers, they're type-returning functions--functions that compute selector values. If you have a way to compute selector values from inputs and a way to specify the order in which matches against them will be tried, and a way to specify how matching is to be performed, then you have the tools to specify any dispatching algorithm you like. Clojure presently provides some tools for this: dispatch functions are type-returning functions; hierarchies are graphs of selector values; isa? and prefer- method are a control language for describing traversal, and isa? is also a matching function. But these are restricted versions: dispatch functions are meant to return a restricted range of values--symbols, keywords, classes, and certain collections of those values; multifunction dispatch supports hierarchies, but not other kinds of graphs; isa? and prefer-method are the only vocabulary we have for describing traversal, and isa? is the only matcher (almost the first thing I wanted to do when using MultiFns was replace isa? with my own matching function). The tools can be more expressive. Multifunction dispatch can be built on user-specified traversals of user-designed graphs of selector values, computed by user-supplied type-returning functions, and using user-specified matchers. The traversal order, the range of selector values, the language for describing traversal, and the matching procedure can all be more first-class and more orthogonal. Special cases can be provided for specific combinations known to be especially useful or efficient. With a full set of tools for describing dispatch, any differences of individual taste in function dispatching and type relationships becomes moot, because the tools are there to implement whatever type relationships and dispatching algorithm you want. Rich doesn't want to enshrine a particular traversal order in Clojure; I want the power to comprehensively specify traversal order in advance for certain uses; very well, any traversal order (or no traversal order) may be defined, as you like. He wants function dispatching and data definitions to be separated and so do I; very well, we have the freedom to compute whatever values we wish for use as types, and to arrange them in whatever sorts
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On 31.03.2009, at 18:50, Mark Engelberg wrote: On Tue, Mar 31, 2009 at 9:45 AM, Konrad Hinsen konrad.hin...@laposte.net wrote: I think this should be sufficient to cover all cases you mentioned, but of course it needs to be tried in practice. I think your idea of specifying a sequence of items to try in the dispatching function, at the point of definition for the multimethod, violates the principle of allowing library consumers to easily extend this on their own without having to contact the library designer. Maybe I wasn't clear about one point: the return sequence is not the sequence of concrete implementations to try (the dispatch function wouldn't even necessarily know them), but a sequence of starting points in the hierarchy from which the standard lookup would start. Konrad. --~--~-~--~~~---~--~~ 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 -~--~~~~--~~--~--~---
Re: Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
On Mar 28, 6:30 pm, David Nolen dnolen.li...@gmail.com wrote: Having thought a little about multiple inheritance when implementing Spinoza I also ran into this problem. However at the time I wasn't hindered by multifn dispatch as much as the fact that parents cannot be ordered (because calling parents on a tag returns a set) as pointed out by Mark. I understand Rich's point that hierarchies probably shouldn't be directional, but for the sake of practicality why not allow the programmer to specify that a tag (or even the whole hierarchy) should in fact have its relationships be ordered? In general prefer-method seemed to me (I could be wrong) to be a last attempt solution- when there is just no other way to resolve an ambiguity, and this ambiguity is not of the hierarchy, but of multimethod dispatch. As pointed out by Konrad Rich, you can work around the limitation of hierarchies right now with a lot of prefer-methods. But as Mikel points out, with current state of prefer-method you have to hunt through code to find out where these prefers were made and for what reason and they assume that you've already written your methods (this for me is the deal breaker). In anycase my general feeling (in agreement with Mark) is that the problem isn't with multifns but rather with how hierarchies are constructed. So the achilles heel of hierarchies seems to be that when you call parents (and related fns) on a tag you get a set - it has no order. For single inheritance hierarchies this is fine, but for multiple inheritance I think this becomes a problem a very fast. Working on Spinoza, I realized I would have to keep my own ordered list of parents in order to support left-right precedence, basically keeping an ordered copy of the whole hierarchy. Again prefer-method is only useful for fixing method-level ambiguities. There's no general way to say, type A always precedes type B without writing quite a bit of explicit code (or hiding the problem away with macros). Here's something that might kick of a dicussion to a proper solution (I am sure there are some gaps in my logic), why not allow something like the following? ;; allow control of how tags get introduced into the hierarchy (def h (make-hierarchy :parents-fn fn1 :ancestors-fn fn2 :descendants-fn fn3)) ;; add a new parent insertion fn to a hierarchy (add-parents-fn h fn4) ;; - returns a new hierarchy with the parents fn set, all parents sets are converted into vectors ;; add a new parent insertion fn to a hierarchy for a specific key, only the parents of ::my-tag are converted into a vector (add-parents-fn h fn4 ::my-tag) ;; a hierarchy fn might look something like the following (defn my-parents-fn [parents new-key] (conj parents new-key)) If my thinking is correct this is pretty flexible. This doesn't break the meaning of hierarchy for any existing code. It doesn't change the signature of multimethods. Yet now a user can provide fns that sorts a hierarchy anyway they please, and it doesn't even have to be the entire hierarchy. This code will probably live in one spot where anyone could look to understand how the hierarchy is being constructed. All high-level precedence ambiguities can now be resolved by looking at the hierarchy. prefer-method does only what it was intended to do- resolve method level ambiguity. You can build a working generic functions implementation for any domain that obeys a protocol consisting of four functions: 1. fn [v [context]] - t A type-returning function that returns a usable type-value for any input in its intended domain. 2. fn [t1 t2 [context]] - Boolean A type-ordering function that returns true if t1 is to be considered more specific than t2. This function must yield values that permit a stable, monotonic sort of types in the domain. 3. fn [t [context]] - s Where s is a sequence of type-values. A supertypes function that, given a type, returns a sequence of all types that are to be considered supertypes of it. 4. fn [inits [context]] - t A type-creating function that returns new type-values that function (1) can produce when applied to domain values, that (2) can order, and that (3) can take as input to yield a canonical list of supertypes. The optional context argument is necessary because some domains are restricted, and operate only with respect to some specific context. An example is a domain defined in terms of one or more ad hoc hierarchies. If you can implement this protocol for a domain, then you can have generic functions that are defined for that domain, and you can have deterministic dispatch and next-method for values in that domain. There's no obvious way to implement (2) for types defined using Clojure's ad hoc hierarchies, because (parents t) returns a set, which is unordered. That doesn't mean there's no way at all to implement (2); it means that any working implementation of (2) imposes an
Possible Solution for Left-Right Precedence and More when using Multimethods? (was re: oo)
Having thought a little about multiple inheritance when implementing Spinoza I also ran into this problem. However at the time I wasn't hindered by multifn dispatch as much as the fact that parents cannot be ordered (because calling parents on a tag returns a set) as pointed out by Mark. I understand Rich's point that hierarchies probably shouldn't be directional, but for the sake of practicality why not allow the programmer to specify that a tag (or even the whole hierarchy) should in fact have its relationships be ordered? In general prefer-method seemed to me (I could be wrong) to be a last attempt solution- when there is just no other way to resolve an ambiguity, and this ambiguity is not of the hierarchy, but of multimethod dispatch. As pointed out by Konrad Rich, you can work around the limitation of hierarchies right now with a lot of prefer-methods. But as Mikel points out, with current state of prefer-method you have to hunt through code to find out where these prefers were made and for what reason and they assume that you've already written your methods (this for me is the deal breaker). In anycase my general feeling (in agreement with Mark) is that the problem isn't with multifns but rather with how hierarchies are constructed. So the achilles heel of hierarchies seems to be that when you call parents (and related fns) on a tag you get a set - it has no order. For single inheritance hierarchies this is fine, but for multiple inheritance I think this becomes a problem a very fast. Working on Spinoza, I realized I would have to keep my own ordered list of parents in order to support left-right precedence, basically keeping an ordered copy of the whole hierarchy. Again prefer-method is only useful for fixing method-level ambiguities. There's no general way to say, type A always precedes type B without writing quite a bit of explicit code (or hiding the problem away with macros). Here's something that might kick of a dicussion to a proper solution (I am sure there are some gaps in my logic), why not allow something like the following? ;; allow control of how tags get introduced into the hierarchy (def h (make-hierarchy :parents-fn fn1 :ancestors-fn fn2 :descendants-fn fn3)) ;; add a new parent insertion fn to a hierarchy (add-parents-fn h fn4) ;; - returns a new hierarchy with the parents fn set, all parents sets are converted into vectors ;; add a new parent insertion fn to a hierarchy for a specific key, only the parents of ::my-tag are converted into a vector (add-parents-fn h fn4 ::my-tag) ;; a hierarchy fn might look something like the following (defn my-parents-fn [parents new-key] (conj parents new-key)) If my thinking is correct this is pretty flexible. This doesn't break the meaning of hierarchy for any existing code. It doesn't change the signature of multimethods. Yet now a user can provide fns that sorts a hierarchy anyway they please, and it doesn't even have to be the entire hierarchy. This code will probably live in one spot where anyone could look to understand how the hierarchy is being constructed. All high-level precedence ambiguities can now be resolved by looking at the hierarchy. prefer-method does only what it was intended to do- resolve method level ambiguity. David --~--~-~--~~~---~--~~ 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 -~--~~~~--~~--~--~---