<br><br>I'm bashing together a simple command line geometry thing that let's you create shapes, move them around them, delete and edit them, test for intersections, etc. The motivation is to get a grip on Haskell IO and how it interacts with user defined datatypes. I'm using GHC.<br>
<br>The problem, which I've tried to show as simply as possible in the abstracted code below, is that something seems to go horribly wrong with un-boxing (is this the correct terminology?) user defined type X's from IO(X)'s. In the example below:<br>
<br>- enterCircle makes a Shape using the Circle cstr and boxes it in a return. And the type checker is happy with the function. <br><br>- But when the Shape is un-boxed in mainLoop then Haskell's type system (or GHC's?) has lost track of its Shapeness. Instead it sees it as a Pt2->Float. Which makes some sort of sense, but is useless (or at least needs ugly hacks to overcome) if you want to put your Circle in a List of Shapes.<br>
<br>- Even worse then the loss of Shapeness is that Haskell is letting me type enterCircle as returning an IO(Shape) - indeed that's what :t gives me in GCHI - but then disagrees with this type in mainLoop! This isn't what I was expecting from the vaunted Haskell type system; can someone explain this contradiction?<br>
<br>So. Does return fail to wrap up type info about user defined types in general? Is this correct Haskell behaviour? Or have I done something wrong?<br><br>If I haven't done something wrong, is there a standard idiom for overcoming this? I suppose I could use a show to convert the Circle to a string and then a read to convert it back in mainLoop's where, but this will mean uglier branching than I planned (the idea was to have a Map of fns that return shapes and just one branch in mainLoop to handle every kind of shape.) And it seems to make something of a nonsense of Haskell's type system as the entire Shape hierarchy will be reduced to IO(String) for these operations.<br>
<br><br><br><br><br><br>import Data.Map as Map<br> <br>data Pt2 = Pt2 {x::Float, y::Float} deriving (Show, Read)<br><br>data Shape = Circle {origin::Pt2, radius::Float}<br> | Square {origin::Pt2, side ::Float}<br>
| Rect {origin::Pt2, other ::Pt2} <br> deriving (Show, Read)<br><br>enterCircle :: IO(Shape)<br>enterCircle = return (r) -- needs to rtn io (shape) because will take input here<br> where r = Circle{origin=Pt2{x=1,y=2}, radius=4}<br>
<br><br>mainLoop :: [Shape] -> IO (String) <br>mainLoop shapes = do<br> putStrLn "?"<br> cmd <- getLine<br> if cmd=="q"<br> then<br> return ("done")<br>
else do<br> y <- enterCircle <br> mainLoop shapes' <br> where shapes' = y : shapes<br> <br>main = mainLoop []<br><br><br><br>