Re: [Haskell] Dynamic binding
Pal-Kristian Engstad wrote:
On Wednesday 22 June 2005 05:38 pm, Andrew Ward wrote:
What would be the normal way for a Haskell programmer to handle the typical shape example in beginner OO tutorials?
By not doing OO. You have to ask yourself, what is the purpose and/or benefit of using OO? In C++, OO is _useful_ because you restrict the problems of mutable data (by enclosing it in C++ classes).
ML type languages have other methods of doing things, and guess what, OO is not that needed for these languages. Sum-types, pattern-matching and data constructors make half of the need for OO go away. Higher order functions and make it even less needed. For the rest, there's always work-arounds.
PKE.
To handle the problem of drawing all shapes, in c++, I would have a list of shape pointers: struct shape{ virtual void draw(...);}; struct circle : public shape {...}; struct square : public shape {...}; std::list<shape *> shapes; for(std::list<shape *>::iterator it = shapes.begin();it != shapes.end();++it) { (*it)->draw(...); } This general pattern of dynamic binding I use over and over again. Could you give me some example code of this type of thing handled in Haskell's way? Assuming that the number of classes deriving from shape might get quite large. Andrew Ward.
On Wednesday 22 June 2005 06:38 pm, Andrew Ward wrote: % This general pattern of dynamic binding I use over and over again. Could % you give me some example code of this type of thing handled in Haskell's % way? Assuming that the number of classes deriving from shape might get % quite large. What is "large"? Less than, say, 30? If so:
module Shapes where
type Position = (Float, Float)
data Shape = Circle Position Float | Square Position Float
prim_draw_circle x y radius = return ("circle(" ++ show x ++ ", " ++ show y ++ ", " ++ show radius ++ ")")
prim_draw_rect x0 y0 x1 y1 = return ("square(" ++ show x0 ++ ", " ++ show y0 ++ ", " ++ show x1 ++ ", " ++ show y1 ++ ")")
draw :: Shape -> IO String draw (Circle (x, y) radius) = prim_draw_circle x y radius draw (Square (x, y) size) = prim_draw_rect x y (x+size) (y+size)
drawShapes :: [Shape] -> IO String drawShapes (x:xs) = do s <- draw x putStrLn s drawShapes xs drawShapes [] = return ""
shapes = [ Circle (0.0, 0.0) 1.0, Circle (1.0, 1.0) 2.0, Square (0.0, 0.0) 2.0 ]
main = drawShapes shapes
PKE. -- _ \`. Pål-Kristian Engstad, Lead Programmer, \ `| Naughty Dog, Inc., 1601 Cloverfield Blvd, 6000 North, __\ |`. Santa Monica, CA 90404, USA. (310) 633-9112. / /o mailto:engstad@naughtydog.com http://www.naughtydog.com / '~ mailto:mrengstad@yahoo.com http://www.engstad.com / ,' Hang-gliding Rulez! ~'
Hello Andrew, Thursday, June 23, 2005, 5:38:03 AM, you wrote: AW> To handle the problem of drawing all shapes, in c++, I would have a list AW> of shape pointers: AW> struct shape{ virtual void draw(...);}; AW> struct circle : public shape {...}; AW> struct square : public shape {...}; AW> std::list<shape *> shapes; AW> for(std::list<shape *>::iterator it = shapes.begin();it != AW> shapes.end();++it) { (*it)->>draw(...); } AW> This general pattern of dynamic binding I use over and over again. Could AW> you give me some example code of this type of thing handled in Haskell's AW> way? Assuming that the number of classes deriving from shape might get AW> quite large. just create list of draw functions itself: [drawCircle (10,10) 5, drawSquare (20,20) 10] you are not expected that this problem can be solved with one line of code? :) for more complex tasks - declare interface as a structure: data ShapeInterface = Shape { draw :: IO (), moveTo :: Point -> IO (), calcArea :: Float } and return this structures from "constructor" functions: circle x y r = Shape { draw = drawCircle center r, moveTo newCenter = ...., calcArea = pi*r*r } where center = Point x y square x y size = Shape { draw = ...., moveTo newCenter = ...., calcArea = size*szie } figures = [circle 1 2 3, square 4 5 6, circle 7 8 9] of course, you cannot use inherited field names when using this technique :) -- Best regards, Bulat mailto:bulatz@HotPOP.com
G'day all. Thursday, June 23, 2005, 5:38:03 AM, you wrote:
To handle the problem of drawing all shapes, in c++, I would have a list of shape pointers:
struct shape{ virtual void draw(...);}; struct circle : public shape {...}; struct square : public shape {...}; std::list<shape *> shapes; for(std::list<shape *>::iterator it = shapes.begin();it != shapes.end();++it) { (*it)->>draw(...); }
This general pattern of dynamic binding I use over and over again. Could you give me some example code of this type of thing handled in Haskell's way? Assuming that the number of classes deriving from shape might get quite large.
class Drawable s where draw :: s -> IO () data Circle = Circle Point Radius instance Drawable Circle where draw (Circle centre radius) = ... If you only need interface inheritance, this should do. If you also need implementation inheritance, then you can model it with an upcast method: data Shape = Shape Stuff class Shape s where toShape :: s -> Shape draw :: s -> IO () instance Shape Shape where toShape s = s draw s = ... data Circle = Circle Shape Point Radius instance Shape Circle where toShape (Circle s _ _) = s draw (Circle _ centre radius) = ... In your original example, draw() wasn't abstract virtual, so I assume there's a reasonable default draw() method for your "shape" class. If there isn't, then it's probably better in Haskell to split the typeclass: class Shape s where toShape :: s -> Shape class (Shape s) => DrawableShape s where draw :: s -> IO () And only define DrawableShape on types where "draw" makes sense. Cheers, Andrew Bromage
On Jun 22, 2005, at 9:38 PM, Andrew Ward wrote:
Pal-Kristian Engstad wrote:
On Wednesday 22 June 2005 05:38 pm, Andrew Ward wrote:
What would be the normal way for a Haskell programmer to handle the typical shape example in beginner OO tutorials?
By not doing OO. You have to ask yourself, what is the purpose and/or benefit of using OO? In C++, OO is _useful_ because you restrict the problems of mutable data (by enclosing it in C++ classes).
ML type languages have other methods of doing things, and guess what, OO is not that needed for these languages. Sum-types, pattern-matching and data constructors make half of the need for OO go away. Higher order functions and make it even less needed. For the rest, there's always work-arounds.
PKE.
To handle the problem of drawing all shapes, in c++, I would have a list of shape pointers:
struct shape{ virtual void draw(...);}; struct circle : public shape {...}; struct square : public shape {...}; std::list<shape *> shapes; for(std::list<shape *>::iterator it = shapes.begin();it != shapes.end();++it) { (*it)->draw(...); }
This general pattern of dynamic binding I use over and over again. Could you give me some example code of this type of thing handled in Haskell's way? Assuming that the number of classes deriving from shape might get quite large.
It seems to me that if this is the specific problem being addressed, a list of higher-order functions is exactly the right solution in any ML-like language, and this is why there have been several responses to that effect. I do think it's fair to say "consider changing the way you think" to OO programmers trying to learn Haskell. If we were on an OOP mailing list, I could ask for days how to simulate pattern matching and algebraic types---and get a nonsensical runaround involving the visitor pattern and huge swaths of unreadable code. Ralf, I think it's incumbent on you, having said several times "that isn't solving the problem", to more clearly explain what problem you think exists and cannot be solved gracefully (I suspect it has to do with extensible down-casting---which is indeed hard in Haskell, but many reasonable people might consider irrelevant or even overtly bad). Andrew, if this isn't the problem you're actually trying to solve, can you explain why a simple list of functions doesn't help you? -Jan-Willem Maessen
Andrew Ward.
_______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
participants (5)
-
ajb@spamcop.net -
Andrew Ward -
Bulat Ziganshin -
Jan-Willem Maessen -
Pal-Kristian Engstad