Re: Polymorphic functions in Clojure (or how to stop thinking in objects)...
Hi, I wanted to take the opportunity to thank the people who responded to my question on thinking beyond O-O. The replies form a very useful slice through Clojure design strategies and idiomatic use of the language. Thanks! Stu -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Mar 14, 8:15 pm, Daniel Solano Gomez cloj...@sattvik.com wrote: I believe there are two approaches to doing this in Clojure: 1. Multimethods:http://clojure.org/multimethods 2. Protocols:http://clojure.org/Protocols Of the two, as of Clojure 1.2, protocols are the preferred way of doing things. What? Who says they're preferred? Prefer the one that is simplest, unless you really need performance, in which case use protocols. They handle the simplest and most common case of polymorphism: by type. ;; snip protocol suggestions, which were fine I might be inclined to do this as a multimethod dispatching on the number of points in the shape. Maybe I represent my shape as a vector: [[p1, p2, ...] more-data]. Then I can write: (defmulti draw (fn [pts more] (count pts)) (defmethod draw 1 [[center] radius] (do stuff with these)) (defmethod draw 2 [[p1 p2]] (draw a line)) (defmethod draw 3 [[p1 p2 p3] filled?] (draw a triangle)) ... (defmethod draw :default [points] (connect up all the points)) In fact a lot of the methods won't need to even be written, because :default can handle them by just drawing lines from point to point. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
Another choice is to construct shapes as closures with auto-dispatch. So a circle could be made thus, with no data structure per se: (defn make-circle [x y r] (fn [method] (case method :draw (fn [color] ...) :rotate (fn [degrees] ...) :r (fn [] r) :x (fn [] x) :y (fn [] y Then the API would just delegate to each object: (defn draw [shape color] ((shape :draw) color)) (defn rotare [shape degrees] ((shape :rotate) degrees)) And is used like this: (def circle (make-circle 5 5 10)) (draw circle) On Mar 15, 9:26 am, Alan a...@malloys.org wrote: On Mar 14, 7:54 pm, stu stuart.hungerf...@gmail.com wrote: Hi, I'd like to create a simple library of drawable shapes: lines, circles and rectangles. I've placed each type of shape in its own namespace with functions that operate on that shape kind: (ns myshapes.line) (defn line ... creates new line ...) (defn draw ... draws a line ...) To keep things simple a line is just a vector of two points and circles and rectangles are just structs. I'd also like to have a sequence of shapes called a picture: (ns myshapes.picture) (defn picture ... returns picture sequence from args ...) (defn draw ... draws whole picture) What's the idiomatic way of handling a situation like this in Clojure? Do I need to use richer data structures than vectors and structs for the shapes so they carry some kind of type information? I can see how a draw multi-method would work if the individual shapes could be distinguished, or am I going about this the wrong way? I think what you need is *less* rich data types, as I outline in my answer (sorry, didn't notice this part of the question or I'd have combined the two). Instead of special structs for each type, just have every shape carry information in similar ways; then you can write a draw-shape function that works with any old shape, not needing to know much about them. If you want to be more explicit you can define all shapes as a struct of {:points [p1 p2...], :whatever more-data, :and- also even-more}, but it will help your dispatch immensely if there's a single, coherent way to get a shape's points regardless of its types. Then if you ever have to put special logic that depends on something other than the points in the shape (eg circles have a radius), you can turn that draw-shape function into a multimethod and have the :default dispatch value do all the boring drawing.- Hide quoted text - - Show quoted text - -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
Wow, it's like I'm reading Let Over Lambda all over again :) On Mar 15, 9:58 am, Armando Blancas armando_blan...@yahoo.com wrote: Another choice is to construct shapes as closures with auto-dispatch. So a circle could be made thus, with no data structure per se: (defn make-circle [x y r] (fn [method] (case method :draw (fn [color] ...) :rotate (fn [degrees] ...) :r (fn [] r) :x (fn [] x) :y (fn [] y Then the API would just delegate to each object: (defn draw [shape color] ((shape :draw) color)) (defn rotare [shape degrees] ((shape :rotate) degrees)) And is used like this: (def circle (make-circle 5 5 10)) (draw circle) On Mar 15, 9:26 am, Alan a...@malloys.org wrote: On Mar 14, 7:54 pm, stu stuart.hungerf...@gmail.com wrote: Hi, I'd like to create a simple library of drawable shapes: lines, circles and rectangles. I've placed each type of shape in its own namespace with functions that operate on that shape kind: (ns myshapes.line) (defn line ... creates new line ...) (defn draw ... draws a line ...) To keep things simple a line is just a vector of two points and circles and rectangles are just structs. I'd also like to have a sequence of shapes called a picture: (ns myshapes.picture) (defn picture ... returns picture sequence from args ...) (defn draw ... draws whole picture) What's the idiomatic way of handling a situation like this in Clojure? Do I need to use richer data structures than vectors and structs for the shapes so they carry some kind of type information? I can see how a draw multi-method would work if the individual shapes could be distinguished, or am I going about this the wrong way? I think what you need is *less* rich data types, as I outline in my answer (sorry, didn't notice this part of the question or I'd have combined the two). Instead of special structs for each type, just have every shape carry information in similar ways; then you can write a draw-shape function that works with any old shape, not needing to know much about them. If you want to be more explicit you can define all shapes as a struct of {:points [p1 p2...], :whatever more-data, :and- also even-more}, but it will help your dispatch immensely if there's a single, coherent way to get a shape's points regardless of its types. Then if you ever have to put special logic that depends on something other than the points in the shape (eg circles have a radius), you can turn that draw-shape function into a multimethod and have the :default dispatch value do all the boring drawing.- Hide quoted text - - Show quoted text - -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Mar 14, 7:54 pm, stu stuart.hungerf...@gmail.com wrote: Hi, I'd like to create a simple library of drawable shapes: lines, circles and rectangles. I've placed each type of shape in its own namespace with functions that operate on that shape kind: (ns myshapes.line) (defn line ... creates new line ...) (defn draw ... draws a line ...) To keep things simple a line is just a vector of two points and circles and rectangles are just structs. I'd also like to have a sequence of shapes called a picture: (ns myshapes.picture) (defn picture ... returns picture sequence from args ...) (defn draw ... draws whole picture) What's the idiomatic way of handling a situation like this in Clojure? Do I need to use richer data structures than vectors and structs for the shapes so they carry some kind of type information? I can see how a draw multi-method would work if the individual shapes could be distinguished, or am I going about this the wrong way? I think what you need is *less* rich data types, as I outline in my answer (sorry, didn't notice this part of the question or I'd have combined the two). Instead of special structs for each type, just have every shape carry information in similar ways; then you can write a draw-shape function that works with any old shape, not needing to know much about them. If you want to be more explicit you can define all shapes as a struct of {:points [p1 p2...], :whatever more-data, :and- also even-more}, but it will help your dispatch immensely if there's a single, coherent way to get a shape's points regardless of its types. Then if you ever have to put special logic that depends on something other than the points in the shape (eg circles have a radius), you can turn that draw-shape function into a multimethod and have the :default dispatch value do all the boring drawing. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Tue Mar 15 09:21 2011, Alan wrote: On Mar 14, 8:15 pm, Daniel Solano Gomez cloj...@sattvik.com wrote: I believe there are two approaches to doing this in Clojure: 1. Multimethods:http://clojure.org/multimethods 2. Protocols:http://clojure.org/Protocols Of the two, as of Clojure 1.2, protocols are the preferred way of doing things. What? Who says they're preferred? Prefer the one that is simplest, unless you really need performance, in which case use protocols. Well, preferred may be a strong word, but such seems to be the implication from some the documentation on clojure.org. From the page on protocols, one of the motivations is to ‘support the 90% case of multimethods (single dispatch on type) while providing higher-level abstraction/organization’. In particular, there is the note that ‘most uses of StructMaps would now be better served by records’, and records are designed to work particularly well with protocols. I don't think that protocols introduce a significantly different level of complexity than multimethods. If you are dispatching on type anyway, they give you performance with your polymorphism. If you want some other type of dispatch, multimethods give you the ultimate flexibility in your polymorphism. I might be inclined to do this as a multimethod dispatching on the number of points in the shape. Maybe I represent my shape as a vector: [[p1, p2, ...] more-data]. Then I can write: (defmulti draw (fn [pts more] (count pts)) (defmethod draw 1 [[center] radius] (do stuff with these)) (defmethod draw 2 [[p1 p2]] (draw a line)) (defmethod draw 3 [[p1 p2 p3] filled?] (draw a triangle)) ... (defmethod draw :default [points] (connect up all the points)) In fact a lot of the methods won't need to even be written, because :default can handle them by just drawing lines from point to point. This is an excellent example of how multimethods, combined with a simple and straightforward data type, can accomplish something that protocols can not. In the end, the it all depends on what fits best with what you are doing. One approach is to simply avoid using multimethods or polymorphism as you start developing. Instead, you can use a custom dispatch using if or cond in your polymorphic methods. Once you find your code being littered with such things, you can think about what works best in your situation. Sincerely, Daniel Solano Gómez pgpRS6Sfae2hQ.pgp Description: PGP signature
Re: Polymorphic functions in Clojure (or how to stop thinking in objects)...
Hi stu, take a look in clojure protocols. I think that this can help you. http://clojure.org/protocols http://clojure.org/protocols Luiz Costa http://clojure.org/protocols On Mon, Mar 14, 2011 at 11:54 PM, stu stuart.hungerf...@gmail.com wrote: Hi, I'd like to create a simple library of drawable shapes: lines, circles and rectangles. I've placed each type of shape in its own namespace with functions that operate on that shape kind: (ns myshapes.line) (defn line ... creates new line ...) (defn draw ... draws a line ...) To keep things simple a line is just a vector of two points and circles and rectangles are just structs. I'd also like to have a sequence of shapes called a picture: (ns myshapes.picture) (defn picture ... returns picture sequence from args ...) (defn draw ... draws whole picture) The problem I have is with the myshapes.picture/draw function. As a Clojure newb I keep wanting to think of this like a polymorphic function in the O-O world that relies on each sequence member having a draw function. What's the idiomatic way of handling a situation like this in Clojure? Do I need to use richer data structures than vectors and structs for the shapes so they carry some kind of type information? I can see how a draw multi-method would work if the individual shapes could be distinguished, or am I going about this the wrong way? Any advice much appreciated, Stu -- 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 Note that posts from new members are moderated - please be patient with your first post. 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 -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Mar 15, 10:30 am, Daniel Solano Gomez cloj...@sattvik.com wrote: In the end, the it all depends on what fits best with what you are doing. One approach is to simply avoid using multimethods or polymorphism as you start developing. Instead, you can use a custom dispatch using if or cond in your polymorphic methods. Once you find your code being littered with such things, you can think about what works best in your situation. Seconded. I often know ahead of time I'll want to extend to multimethods eventually, but sometimes I don't. Because multimethods, protocols, and plain-old-functions look the same to the caller, you can write your code whatever way is most straightforward first, and add in higher-octane stuff when you discover you need it. Nobody need ever know. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Tue, Mar 15, 2011 at 10:40 AM, Alan a...@malloys.org wrote: Seconded. I often know ahead of time I'll want to extend to multimethods eventually, but sometimes I don't. Because multimethods, protocols, and plain-old-functions look the same to the caller, you can write your code whatever way is most straightforward first, and add in higher-octane stuff when you discover you need it. Nobody need ever know. Yes, but beware, record constructors are not plain old functions and do not look the same as other sorts of constructors, so that can be an issue if you think you might change representation midway. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Mar 15, 10:50 am, Mark Engelberg mark.engelb...@gmail.com wrote: On Tue, Mar 15, 2011 at 10:40 AM, Alan a...@malloys.org wrote: Seconded. I often know ahead of time I'll want to extend to multimethods eventually, but sometimes I don't. Because multimethods, protocols, and plain-old-functions look the same to the caller, you can write your code whatever way is most straightforward first, and add in higher-octane stuff when you discover you need it. Nobody need ever know. Yes, but beware, record constructors are not plain old functions and do not look the same as other sorts of constructors, so that can be an issue if you think you might change representation midway. This only causes issues if you (a) switch down from protocols to something else, and (b) didn't do records right in the first place (usually I do them wrong!). Going from (defn make-line [p1 p2] {:p1 p1 :p2 p2}) to (defrecord Line [p1 p2] Shape (draw [this] ...)) (defn make-line [p1 p2] (Line. p1 p2)) does not give anyone backwards-compatibility issues. You just make sure there's still a clojure-level first-class function responsible for delegating to the record constructor. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Mon, Mar 14, 2011 at 11:15 PM, Daniel Solano Gomez cloj...@sattvik.com wrote: This method works fairly well, and you can even use it to define protocols for types you don't control, such as classes from a Java API. If you need some more complicated form of dispatch for polymorphism, there is the multimethod approach. And if you need more complex still, you can embed function references in some fields of your objects and call those. This resembles OO methods in that the functions are members of the objects, but you needn't have inheritance; you can have ad hoc objects and arbitrary mixins; and you can have any kind of assignment or function resolution you want. And you can still have top level functions that call your member functions. Really, Lisp's first-class functions cause a panoply of options to open up for modeling objects-with-behavior and polymorphism and let you use styles ranging from fairly OO to nearly pure FP even to procedural (via, for instance, long let bindings that do stepwise computations), or mix and match them to suit. Use protocols and records when they fit well; otherwise, investigate multimethods and explicit use of function pointers. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Tue, Mar 15, 2011 at 12:58 PM, Armando Blancas armando_blan...@yahoo.com wrote: Another choice is to construct shapes as closures with auto-dispatch. So a circle could be made thus, with no data structure per se: (defn make-circle [x y r] (fn [method] (case method :draw (fn [color] ...) :rotate (fn [degrees] ...) :r (fn [] r) :x (fn [] x) :y (fn [] y And then, of course, there's this Smalltalkesque dispatch model. All it needs to complete the picture are a couple of default cases: :DoesNotUnderstand (throw (IllegalArgumentException. (str make-circle DoesNotUnderstand: method))) (recur :DoesNotUnderstand) :) -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Polymorphic functions in Clojure (or how to stop thinking in objects)...
Hi, I'd like to create a simple library of drawable shapes: lines, circles and rectangles. I've placed each type of shape in its own namespace with functions that operate on that shape kind: (ns myshapes.line) (defn line ... creates new line ...) (defn draw ... draws a line ...) To keep things simple a line is just a vector of two points and circles and rectangles are just structs. I'd also like to have a sequence of shapes called a picture: (ns myshapes.picture) (defn picture ... returns picture sequence from args ...) (defn draw ... draws whole picture) The problem I have is with the myshapes.picture/draw function. As a Clojure newb I keep wanting to think of this like a polymorphic function in the O-O world that relies on each sequence member having a draw function. What's the idiomatic way of handling a situation like this in Clojure? Do I need to use richer data structures than vectors and structs for the shapes so they carry some kind of type information? I can see how a draw multi-method would work if the individual shapes could be distinguished, or am I going about this the wrong way? Any advice much appreciated, Stu -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Polymorphic functions in Clojure (or how to stop thinking in objects)...
On Mon Mar 14 19:54 2011, stu wrote: The problem I have is with the myshapes.picture/draw function. As a Clojure newb I keep wanting to think of this like a polymorphic function in the O-O world that relies on each sequence member having a draw function. What's the idiomatic way of handling a situation like this in Clojure? Do I need to use richer data structures than vectors and structs for the shapes so they carry some kind of type information? I can see how a draw multi-method would work if the individual shapes could be distinguished, or am I going about this the wrong way? I believe there are two approaches to doing this in Clojure: 1. Multimethods: http://clojure.org/multimethods 2. Protocols: http://clojure.org/Protocols Of the two, as of Clojure 1.2, protocols are the preferred way of doing things. They handle the simplest and most common case of polymorphism: by type. You can define a set of functions for your shapes: (defprotocol Shape (draw [shape]) (rotate [shape angle]) …) Then you can convert your structs into records. So, instead of something like: (defstruct triangle :side1 :side2 :side3) You now have: (defrecord Triangle [side1 side2 side3]) You can implement your protocol inside of the defrecord itself or use extend or extend-type. An example of doing it inside of defrecord is: (defrecord Triange [side1 side2 side3] Shape (draw [this] ; assumes sides satisfy Shape protocol (draw side1) (draw side2) (draw side3)) (rotate [this] …) …) This method works fairly well, and you can even use it to define protocols for types you don't control, such as classes from a Java API. If you need some more complicated form of dispatch for polymorphism, there is the multimethod approach. Sincerely, Daniel Solano Gómez pgpSND14zVdtO.pgp Description: PGP signature
Re: Polymorphic functions in Clojure (or how to stop thinking in objects)...
If the set of types is closed and will not be extended by users, there's nothing wrong with just writing your own dispatch using cond, something like: (defn draw [shape] (cond (triangle? shape) (draw-triangle shape) (circle? shape) (draw-circle shape) ...)) Then just write your helper functions: triangle?, draw-triangle, circle?, draw-circle and you're good to go. Clearly, no matter what, you need *some* way to distinguish between your different types. It doesn't necessarily have to be a true type slot, as provided by defrecord. It could be that you represent circles, for example, as a map that begins {:shape :circle ...}. But it's true that protocols or multimethods would be the more common way to do this. Protocols are reportedly faster, but not as flexible (e.g., *must* dispatch on type, such as that provided by defrecord, no inheritance, etc.). I still tend to go with multimethods. Multimethods (or rolling your own predicates and dispatch as I described above) would allow you to use the combination of vectors and structs you outlined. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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