<br><br><div class="gmail_quote">On Wed, Apr 18, 2012 at 8:10 AM, umptious <span dir="ltr">&lt;<a href="mailto:umptious@gmail.com">umptious@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
One of the programming exercises I keep evolving as I learn Haskell is a toy 2D shape editor with four primitives:<br><br>data Shape =   Circle   {origin::Pt2, radius::Float}<br>                       | Square   {origin::Pt2, side  ::Float}<br>

                       | Rect     {origin::Pt2, other ::Pt2}<br>                       | Composite {shapes::[Shape]}<br>                         deriving (Show, Read)<br><br>The intent  is Composites can contain Shapes of any kind, including other Composites so that you can apply transformations to a Composite and these will be applied to the contained Shapes recursively. So an arm might contain a hand which constains a dozen or so Rects. Transform the arm and the hand and rects should transform; transform the hand and its rects should transform but the not arm. Big deal.<br>

<br>And the above makes that really easy when you know you&#39;re talking to a Composite. But now I&#39;ve hit an intellectual stumbling point and the books and source I have don&#39;t seem to address it:  I can apply the destructuring command &quot;shapes&quot; defined in the cstr &quot;Composite&quot; to ANY Shape. And if I do that to say a circle, BLAM! Or if I apply &quot;radius&quot; to Rect, BLAM! At runtime. No type checking support (because yes, they&#39;re the same type.)<br>
</blockquote><div><br></div><div>Well, if you have a Shape, you do not know what data type you have and neither does the compiler. However, you can code a function, say shapeList, which always gives you a list of Shapes regardless of what type of Shape gets past in:</div>
<div><br></div><div><div>shapeList :: Shape -&gt; [Shape]</div><div>shapeList (Composite shapes) = shapes</div><div>shapeList s = [s]</div></div><div><br></div><div>Lesson: don&#39;t use record syntax on a heterogeneous collection. I&#39;m surprised the compiler doesn&#39;t complain when record syntax isn&#39;t guaranteed to succeed.</div>
<div><br></div><div>As a general comment, it looks like you are trying to code C++ or Java style OO code in Haskell. I can say from experience, it doesn&#39;t work well. </div><div><br></div><div>Generally, envision your functions to work on a class of abstract data types (ATDs). Generalize this class of ATDs into a typeclass. Write an instance of the function to operate on each ADT you want to be a member of a typeclass. So, if I was going to write some code to handle shapes I might do it like the following. Be warned, I&#39;m far from a Haskell Guru, but I think this is a better approach. Hopefully we&#39;ll get an improved bit of code....</div>
<div><br></div><div><div>import Data.List</div><div><br></div><div>data Pt2 = Pt2 { x :: Float , y :: Float } deriving (Show, Read)</div><div><br></div><div>data Circle =  Circle { originCircle :: Pt2 , radius :: Float } deriving (Show, Read)</div>
<div><br></div><div>data Square =    Square   { originSquare ::Pt2 , side  :: Float } deriving (Show, Read)</div><div><br></div><div>data Rect =      Rect {originRect ::Pt2, other :: Pt2} deriving (Show, Read)</div><div><br>
</div><div>data Composite = Composite { circles :: [Circle]</div><div>                           , squares :: [Square]</div><div>                           , rects   :: [Rect]</div><div>                           }</div><div>
<br></div><div>class Shape a where</div><div>   area :: a -&gt; Float</div><div>   minx :: a -&gt; Float</div><div>   miny :: a -&gt; Float</div><div><br></div><div>instance Shape Circle where</div><div>   area (Circle _ r) = r * r * pi</div>
<div>   minx (Circle (Pt2 x _) r) = x - r</div><div>   miny (Circle (Pt2 _ y) r) = y - r</div><div><br></div><div>instance Shape Square where</div><div>   area (Square _ side) = side*side</div><div>   minx (Square (Pt2 x y) side) = if side &lt; 0</div>
<div>                                     then x + side</div><div>                                     else x</div><div>   miny (Square (Pt2 x y) side) = if side &lt; 0</div><div>                                     then y + side</div>
<div>                                     else y</div><div><br></div><div>instance Shape Rect where</div><div>   area (Rect (Pt2 x1 y1) (Pt2 x2 y2)) = abs((x2 - x1) * (y2 - y1))</div><div>   minx (Rect (Pt2 x1 y1) (Pt2 x2 y2)) = min x1 x2</div>
<div>   miny (Rect (Pt2 x1 y1) (Pt2 x2 y2)) = min y1 y2</div><div><br></div><div>instance Shape Composite where</div><div>   area (Composite cs ss rs) = (sum $ map area cs) + (sum $ map area ss) + (sum $ map area rs)</div>
<div>   minx (Composite cs ss rs) = Data.List.minimum(map minx cs ++ map minx ss ++ map minx rs)</div><div>   miny (Composite cs ss rs) = Data.List.minimum(map miny cs ++ map miny ss ++ map miny rs)</div></div><div><br></div>
<div><br></div><div><br></div></div>