
The composite design pattern implemented using record types, where the named elements are the interface to the object Overall, I think I agree with Tim that the record types are simpler to code. I'm not sure, though, what would happen if I tried to add state to the types. With the previous example, using existentials to create a reference type that holds elements of a type class that matches the interface, I think that it would be natural to hold state by having that element stored in a mutable state variable, and replacing the held values. In any case: Two methods, add and draw
data Component = Component { draw :: String, add :: Component -> Component }
A constructor for the leaf type, which holds a string
leaf :: String -> Component leaf s = Component draw1 add1 where draw1 = show s add1 _ = leaf s
the draw method for the composite type (because I was having trouble with layout and formating for 72 cols)
compositeDraw :: [Component] -> String compositeDraw [] = "()" compositeDraw leaves = "(" ++ (foldr1 (++) $ map draw leaves) ++ ")"
A constructor for the composite type, which holds a list of components and dispatches to the contained elements
composite :: [Component] -> Component composite cs = Component draw1 add1 where draw1 = compositeDraw cs add1 c = composite $ c:cs
On 2/27/07, Tim Docker
interesting. it leads to something that feels much more like an object
as far as haskell is concerned, everything has the same type, even
Steve Downey wrote: based, as opposed to a class based, system. though different instances have very different behavior.
.... the question is, which plays nicer with the rest of haskell? that is, if i'm not committing to a closed dsl, which style is more likely to be reusable against other libraries.
I suspect there's no right answer - it's a case of choosing the best approach for the problem. As an example, my charting library (http://dockerz.net/software/chart.html) uses the record of functions approach for composing drawings:
data Renderable = Renderable { minsize :: (Render RectSize) render :: (Rect -> Render ()) }
Tim