Reputation: 277
So I am trying to write a bit of Haskell and I've come to this problem that makes me wanna smash my head against a wall.
printGrade points = case points of
points | 0 <= points && points < 50 -> 5.0
points | 50 <= points && points < 54 -> 4.0
points | 54 <= points && points < 58 -> 3.7
points | 58 <= points && points < 62 -> 3.3
points | 62 <= points && points < 66 -> 3.0
points | 66 <= points && points < 70 -> 2.7
points | 70 <= points && points < 74 -> 2.3
points | 74 <= points && points < 78 -> 2.0
points | 78 <= points && points < 82 -> 1.7
points | 82 <= points && points < 86 -> 1.3
points | 86 <= points && points < 100 -> 1.0
note a b c d =
if d > 100 || c > 20
then return "Wrong input"
else if a == False || b == False
then printGrade d
else printGrade (c + d)
When I try to run the code it compiles without problems but actually calling the function brings this error
<interactive>:91:1: error:
• Ambiguous type variable ‘m0’ arising from a use of ‘print’
prevents the constraint ‘(Show (m0 [Char]))’ from being solved.
Probable fix: use a type annotation to specify what ‘m0’ should be.
These potential instances exist:
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
instance (Show a, Show b) => Show (a, b) -- Defined in ‘GHC.Show’
instance (Show a, Show b, Show c) => Show (a, b, c)
-- Defined in ‘GHC.Show’
...plus 13 others
...plus two instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In a stmt of an interactive GHCi command: print it
I know that it has something to do with then return "Wrong Input" but I don't know a way to fix this since I have to print out a string at some point. (I've tried show/print/putStrLn which leads to another error)
Upvotes: 1
Views: 997
Reputation: 477180
Frequently, people that start with Haskell, use the return :: Monad m => a -> m a
function, since in the imperative world, nearly all [imperative] programming languages assign an almost equal semantic to the return
keyword. In Haskell however, return
is a function (not a keyword), and it is used in the context of monads. Although monads are quite powerful, I would advice to first take a look at how this structure works before using return
at all. One hint: it does not really work like it works in the imperative world.
We can remove the return
function, like:
note a b c d =
if d > 100 || c > 20
then "Wrong input"
else if a == False || b == False
then printGrade d
else printGrade (c + d)
But then we get a new error:
<interactive>:20:18: error:
• Could not deduce (Fractional [Char])
arising from a use of ‘printGrade’
from the context: (Num a, Ord a)
bound by the inferred type of
note :: (Num a, Ord a) => Bool -> Bool -> a -> a -> [Char]
at <interactive>:(16,1)-(21,35)
• In the expression: printGrade d
In the expression:
if a == False || b == False then
printGrade d
else
printGrade (c + d)
In the expression:
if d > 100 || c > 20 then
"Wrong input"
else
if a == False || b == False then
printGrade d
else
printGrade (c + d)
Now Haskell is having problem with the return type. Indeed printGrade
returns values like 1.0
, which is a Fractional
type (what type exactly can be specified later, but the literal suggest that it should be a Fractional
type). And it says that you return a string as well ("Wrong input"
), and since a String
is not a Fractional
type, there is a mismatch. We can solve this by calling show
on the result of printGrade
(I suggest you rename that function, since the function does not print anything), such that we convert the Fractional
type into a String
, so now we got:
note a b c d =
if d > 100 || c > 20
then "Wrong input"
else if a == False || b == False
then show (printGrade d)
else show (printGrade (c + d))
Now the program will compile, but it is rather inelegant. For example you use a case
in the printGrade
function, but you do not really perform any pattern matching. We can use guards instead, like:
grade :: (Num a, Ord a, Fractional b) => a -> b
grade points | points < 0 = error "too small"
| points < 50 = 5.0
| points < 54 = 4.0
| points < 58 = 3.7
| points < 62 = 3.3
| points < 66 = 3.0
| points < 70 = 2.7
| points < 74 = 2.3
| points < 78 = 2.0
| points < 82 = 1.7
| points < 86 = 1.3
| points < 100 = 1.0
| otherwise = error "too large"
Here we thus use one guard per case, furthermore there is no need to check if the value is for example less than 0
, since in that case the previous guard would have fired.
We can use the same technique for the note
function: use patterns and guards to match the values:
note :: (Ord a, Num a) => Bool -> Bool -> a -> a -> String
note _ _ c d | d > 100 || c > 20 = "Wrong input"
note False False _ d = show (printGrade d)
note _ _ c d = show (printGrade (c + d))
Upvotes: 7