
(First of all, sorry for the double reply...)
2008/10/13 Arun Suresh
Now my client want to write another subclass for Drawable... He can do that in any other file... package.. whatever...
How would he do that in Haskell ??? considering he may not modify the source file in while i have defined the Drawable ADT..
I know it is possible using Type Classes... Have a Drawable Type Class.. etc.. etc.. Is there probably a better way of dooing it ??
That depends on what you want to do. If all you want to do is allow the client to pass Drawable's to your API functions, which then only use the class interface, you can get away without existentials:
class Drawable d where area :: d -> Int draw :: d -> IO ()
foo :: Drawable d -> IO () foo d = do putStrLn $ "This shape has area "++show (area d) draw d
On the other hand, if you want to do something like maintain a list of drawables then you'll need to use existential types (and therefore include {-# OPTIONS -fglasgow-exts #-} at the top of the source file):
data Painting = Painting [(forall d. Drawable d => d)]
addToPainting :: Drawable d => d -> Painting -> Painting addToPainting d (Painting ds) = Painting (d:ds)
emptyPainting :: Painting emptyPainting = Painting []
computeTotalArea :: Painting -> Int computeTotalArea (Painting []) = 0 computeTotalArea (Painting (d:ds)) = area d + computeTotalArea (Painting ds)
drawPainting :: Painting -> IO () drawPainting (Painting []) = return () drawPainting (Painting (d:ds)) = draw d >> drawPainting (Painting ds)
You can then expose just the Painting type as an abstract type (with no exposed constructor), and this is similar to making, say, Drawable *painting; Note that in all these examples (including the C++ one), you *only* get the class interface to work with. The main difference is that in C++, it's a bit easier to downcast, whereas in Haskell it's practically impossible, short of modifying your class interface:
class Drawable d where toCircle :: d -> Maybe Circle toCircle _ = Nothing -- default implementation, so clients needn't implement
instance Drawable Circle where toCircle c = Just c
This could be done more generally as well, if you have a class hierarchy,
class Drawable d => PointyShape d data AnyPointy = PointyShape d => AnyPointy d class Drawable d where toPointy :: d -> Maybe AnyPointy
and so on. But beware the difference between the two signatures Drawable a => a -> a -> ... (Drawable a,Drawable b) => a -> b -> ... as pointed out in the OOP vs type classes article. The former needs a guarantee that the two arguments are the SAME drawable, and once you're in an existential, you've lost that information (again, short of doing some hokey stuff with extra class methods, i.e. convert :: Drawable d' => d -> d' -> Maybe d but that's just really ugly and you probably don't want to do that...) Hope this helps, steve