[Haskell-cafe] OO Design in Haskell Example (Draft)

Steve Downey sdowney at gmail.com
Sun Feb 25 16:41:33 EST 2007


In the last OO design in Haskell thread (and probably in every one
preceeding it), it was suggested that having some examples might be a good
idea.

Since most people with existing designs will have some familiarity with
Design Patterns, and those are typical building blocks for OO designs, it
occured to me that implementing some of them might be a useful excersize. If
for nothing other than learning some more Haskell.

Now, some of them are probably bad ideas for implementing in Haskell.
There's a better, or more natural, way than what is suggested by the design
pattern. Visitor is probably not a good pattern to follow, for example. On
the other hand, others may still be useful, even in a functional language.

So, I've been working on a Composite example. I've used existential types to
have a generic proxy to the base type, rather than a simple algebraic type,
since adding new leaves to the algebraic type means modifying the whole
type, a violation of the Open-Closed principle (open for extension, closed
for modification)

The interface of the composite. Two methods, add and draw.

> class IComponent e where
>     draw ::e -> String
>     add :: (IComponent e') => e -> e' -> Component


A proxy type which can hold any kind of component, and provides the
'virtual' dispatch implementation. That is, it forwards to the add
or draw implementation of the instance it is proxying for.

> data Component =
>     forall e.(IComponent e) => Component e

> componentDraw :: Component -> String
> componentDraw (Component c) = draw c

> componentAdd :: (IComponent e) => Component -> e -> Component
> componentAdd (Component e) a  = Component (add e a)

> instance IComponent Component where
>     draw = componentDraw
>     add = componentAdd


The Single type, which is the leaf node in this composite, add is a
no-op, except for wrapping the value in a Component. Since there
isn't an implicit down cast from the 'derived' Single to the 'base'
Component.

> data Leaf =
>     Text String
>     deriving(Show, Eq)

> leafDraw :: Leaf -> String
> leafDraw (Text s) = show s

> leafAdd :: (IComponent e) => Leaf -> e -> Component
> leafAdd s _  = Component s

> instance IComponent Leaf where
>     draw = leafDraw
>     add = leafAdd



The Composite type, which holds a list of Components through the
composite proxy. I was tempted to make the list a state variable,
so that add could modify rather than produce a new Many, but I
wanted to get the basics working.

> data Composite =
>     Many [Component]

> compositeDraw :: Composite -> String
> compositeDraw (Many [])  = "()"
> compositeDraw (Many leaves)  = "(" ++ (foldr1 (++) $ map draw leaves) ++
")"

> compositeAdd :: (IComponent e) => Composite -> e -> Component
> compositeAdd (Many leaves) c =
>     Component $ Many ((Component c) : leaves)

> instance IComponent Composite where
>     draw = compositeDraw
>     add = compositeAdd
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20070225/f0dd8641/attachment.htm


More information about the Haskell-Cafe mailing list