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
Steve Downey wrote:
> interesting. it leads to something that feels much more like an object
based, as opposed to a class based, system.
> as far as haskell is concerned, everything has the same type, even
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