Search code examples
functionhaskelltypestype-declaration

Why is this considered a type mismatch (or error)?


This is the code I have which results in the error that follows:

import Prelude hiding (div)

data Expr = Expr Op Int Int deriving (Show)
data Op = Add | Sub | Mul | Div deriving (Show)


evaluate :: (Num a) => Expr -> a
evaluate (Expr Add a b) = a + b
--evaluate (Expr Sub a b) = sub a b
--evaluate (Expr Mul a b) = mul a b
--evaluate (Expr Div a b) = div a b

Error message:

    exprs.hs:8:27: error:
    • Couldn't match expected type ‘a’ with actual type ‘Int’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          evaluate :: forall a. Num a => Expr -> a
        at exprs.hs:7:1-32
    • In the expression: a + b
      In an equation for ‘evaluate’: evaluate (Expr Add a b) = a + b
    • Relevant bindings include
        evaluate :: Expr -> a (bound at exprs.hs:8:1)
  |
8 | evaluate (Expr Add a b) = a + b
  |                           ^^^^^
Failed, 0 modules loaded.

However, the (+) function has type (Num a) => a -> a -> a, and the pattern I'm matching in the function evaluate has two Ints (a & b) which are both part of the Num typeclass. Since the result of calling (+) on a & b will be of type Int (from the Num typeclass) and that is also what I'm declaring the type of the output of my evaluate function to be, why is GHCi giving me this error?

Note that, if I change the type declaration of evaluate to

evaluate :: Expr -> Int

then this error does not come up.


Solution

  • evaluate :: (Num a) => Expr -> a
    

    states that for any type a which has a Num instance, evaluate can return a value of type a given an Expr value. However given the definition of Expr, you can only ever return an Int, and the compiler is therefore rejecting your definition.

    If it were allowed you would be able to do:

    evaluate (Expr Add 2 3) :: Double
    

    which your definition cannot satisfy.

    You can use your definition if you allow Expr to be parameterised by the expression type:

    data Expr a = Expr Op a a deriving (Show)
    
    evaluate :: (Num a) => Expr a -> a
    evaluate (Expr Add a b) = a + b
    ...