1
votes

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)

1
Why do you use a case here, but only use guards?Willem Van Onsem
hahahah I DONT KNOW when I remove case it doesnt work at allHaskellPlease
And unfortunately, this error shows up a lot. return is not a keyword in Haskell: it is a function that is used for monads. Therefore I advice you not to use it, unless you know what you are doing.Willem Van Onsem
anything I can use instead of return to print out a string without causing this error?HaskellPlease
You should start writing these functions starting from their type. If the return type is, say, Double, you can not return a string (and you surely can not return a string inside a monad like return "abc"). If you want to error out you can use if ... then 2.71 else error "message", but understand that this error will crash the program when evaluated, and the the caller won't be able to catch the error and handle it. Sometimes this is enough. The expression error ".." fits any type.chi

1 Answers

6
votes

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))