Haskell: Operations on Double and Int give error “couldn't match expected type”-Collection of common programming errors

I’m writing a interpreter for my own language and I have an abstract syntax tree which has this type:

data Expression = 
  PInt Int
  | PFloat Double
  | PString String
  | PChar Char
  | PBool Bool
  | Var String
  | Unbound String String
  | Unary String Expression
  | Binary String Expression Expression
  | Call Expression [Expression]
  | Lambda Expression
  | Assign String Expression Expression
  | Conditional Expression Expression Expression
  deriving Eq

I’m trying to write an instance of Num for my class so that I can use existing machinery for numerical operations. Here’s what I’ve written:

instance Num Expression where
   PInt a + PInt b = PInt $ a + b
   PInt a + PFloat b = PFloat $ a + b
   PFloat a + PInt b = PFloat $ a + b
   PFloat a + PFloat b = PFloat $ a + b
   _ + _ = undefined
   PInt a - PInt b = PInt $ a - b
   PInt a - PFloat b = PFloat $ a - b
   PFloat a - PInt b = PFloat $ a - b
   PFloat a - PFloat b = PFloat $ a - b
   _ - _ = undefined
   PInt a * PInt b = PInt $ a * b
   PInt a * PFloat b = PFloat $ a * b
   PFloat a * PInt b = PFloat $ a * b
   PFloat a * PFloat b = PFloat $ a * b
   _ * _ = undefined
   negate (PInt a) = PInt (-a)
   negate (PFloat a) = PFloat (-a)
   negate _ = undefined
   abs (PInt a) = PInt $ abs a
   abs (PFloat a) = PFloat $ abs a
   abs _ = undefined
   signum (PInt a) = PInt $ signum a
   signum (PFloat a) = PFloat $ signum a
   signum _ = undefined
   fromInteger i = (PInt $ fromInteger i)

This gives me errors specifically in the places where I’ve combined ints and floats.

Prelude> :load AST.hs
[1 of 1] Compiling AST          ( AST.hs, interpreted )

AST.hs:38:36:
    Couldn't match expected type `Double' with actual type `Int'
    In the first argument of `(+)', namely `a'
    In the first argument of `PFloat', namely `(a + b)'
    In the expression: PFloat (a + b)

AST.hs:39:37:
    Couldn't match expected type `Double' with actual type `Int'
    In the second argument of `(+)', namely `b'
    In the second argument of `($)', namely `a + b'
    In the expression: PFloat $ a + b

AST.hs:43:33:
    Couldn't match expected type `Double' with actual type `Int'
    In the first argument of `(-)', namely `a'
    In the second argument of `($)', namely `a - b'
    In the expression: PFloat $ a - b

AST.hs:44:37:
    Couldn't match expected type `Double' with actual type `Int'
    In the second argument of `(-)', namely `b'
    In the second argument of `($)', namely `a - b'
    In the expression: PFloat $ a - b

AST.hs:48:33:
    Couldn't match expected type `Double' with actual type `Int'
    In the first argument of `(*)', namely `a'
    In the second argument of `($)', namely `a * b'
    In the expression: PFloat $ a * b

AST.hs:49:37:
    Couldn't match expected type `Double' with actual type `Int'
    In the second argument of `(*)', namely `b'
    In the second argument of `($)', namely `a * b'
    In the expression: PFloat $ a * b
Failed, modules loaded: none.

This doesn’t make sense to me, since the type of an Int + Double in Haskell is a Double, so a + b should resolve to a Double, and since the constructor for a PFloat takes a Double, no problem… why is this not the case?

Resolved: using fromIntegral in front of the variables of type Int fixes it.

  1. The mathematical operators in the Num typeclass expect both of their arguments to have the same type, so you’ll have to convert the Int to a Double using fromIntegral before you can add them together.

    For example, replace this

    PInt a + PFloat b = PFloat $ a + b
    

    with this

    PInt a + PFloat b = PFloat $ fromIntegral a + b
    

Originally posted 2013-11-09 21:02:54.