views:

128

answers:

2

I wrote a little Haskell program to find the area of a triangle, primarily to practice custom types, but it keeps throwing the following error on compile:

areafinder.hs:7:4:
    Couldn't match expected type `Triangle' against inferred type `m b'
    In a stmt of a 'do' expression: putStr "Base: "
    In the expression:
        do { putStr "Base: ";
             baseStr <- getLine;
             putStr "Height: ";
             heightStr <- getLine;
             .... }
    In the definition of `getTriangle':
        getTriangle = do { putStr "Base: ";
                           baseStr <- getLine;
                           putStr "Height: ";
                           .... }

I'm not sure where 'm b' comes from, so I'm at a loss here. Why is it throwing this error, and what can I do to fix it? Here is my code:

module Main where

data Triangle = Triangle Double Double -- base, height
getTriangle :: Triangle
getTriangle = do
    putStr "Base: "
    baseStr <- getLine
    putStr "Height: "
    heightStr <- getLine
    let base = read baseStr :: Double
    let height = read heightStr :: Double
    Triangle base height

calcTriangle :: Triangle -> Double
calcTriangle (Triangle base height) = base * height

main = putStrLn ("Area = " ++ show (calcTriangle getTriangle))

Thanks. :)

+9  A: 

The getTriangle function uses IO, so you have to put that in the function signature.

getTriangle :: IO Triangle

Also, the last line should have return, since it's returning a pure value inside an IO function.

return (Triangle base height)

Here are a couple extra tips: Haskell can figure out that base and height are Double, because you pass them to Triangle, so you don't need to explicitly declare them that way. You can use liftM from the Control.Monad module to read the input and convert to Double in one step.

import Control.Monad
getTriangle :: IO Triangle
getTriangle = do
    putStr "Base: "
    base <- liftM read getLine
    putStr "Height: "
    height <- liftM read getLine
    return (Triangle base height)

The main function also appears to mix pure values with IO. Since getTriangle is IO, you can't pass it directly to calcTriangle. Here is a modified main:

main = do tri <- getTriangle
          putStrLn ("Area = " ++ show (calcTriangle tri))

As a footnote, the area of a triangle is base * height / 2, not base * height.

Finally, a more advanced Haskell programmer would probably write getTriangle in terms of liftM2, but this is just a matter of style. Here is how I would write it:

prompt str = putStr (str ++ ": ") >> liftM read getLine
getTriangle = liftM2 Triangle (prompt "Base") (prompt "Height")
Dietrich Epp
Thanks for the great explanation! Functional programming is still a bit difficult for me, but I'm getting it. Also, I have no clue how I forgot the division in the formula. :)
linkmaster03
+3  A: 

Note that you can drop getTriangle :: Triangle and by running :t getTriangle that the hugs/ghci prompt it will tell you what it thinks the types should be.

Tim Green