writing binop to work with int and floats
haskell, typechecking
Solution
This is called higher rank types, basically the type you want is
binop :: (forall a. Num a => a -> a -> a) -> [Value] -> EvalM Value
But what you have is
binop :: forall a. Num a => (a -> a -> a) -> [Value] -> EvalM Value
Do you see the difference? With the first function, the operator is actually polymorphic within the function, it says "Given a function that takes any `Num a` of type `a -> a -> a` ...". The second one says, "For all `a`, given a function from a single arbitrary `a -> a -> a` ...".
Luckily GHC supports higher rank types,
{-# LANGUAGE RankNTypes #-}
...
binop :: (forall a. Num a => a -> a -> a) -> [Value] -> EvalM Value
binop (+) [VFloat a, VFloat b] = return $ VFloat (a + b)
binop (+) [VInt a, VFloat b] = return $ VFloat (fromInteger a + b)
binop (+) [VFloat a, VInt b] = return $ VFloat (a + fromInteger b)
binop (+) [VInt a, VInt b] = return $ VInt (a + b)
binop _ [_,_] = throwError "Currently only adding numbers"
binop _ _ = throwError "Arity Error: Add takes 2 arguments"
However the type inferencer doesn't do so hot with higher rank types, so you will likely have to add explicit signatures.
Problem
I have a parser that I am working on. Without getting into the all the details, I want a function that will add two numeric values do: ``` add [VFloat a, VFloat b] = return $ VFloat (a + b) add [VInt a, VFloat b] = return $ VFloat (fromInteger a + b) add [VFloat a, VInt b] = return $ VFloat (a + fromInteger b) add [VInt a, VInt b] = return $ VInt (a + b) add [_,_] = throwError "Currently only adding numbers" add _ = throwError "Arity Error: Add takes 2 arguments" ``` Cool, works great. Now I want the same function for `-`,`*`,`/`,`<`,`>`,`==`,etc... So I factor out the `+` operator and go to pass in an operator `op :: Num a => a->a->a` right? Well not quite. If I just replace the `+` with `'op'` the type-checker tells me that op is actually `Double -> Double -> Double` based on the first three versions and therefore it cannot be applied to `Integer`s in the fourth version. Two questions: - How do I write `binop :: Num a => (a->a->a) -> [Value]-> EvalM Value` so that it can handle both VInt and VFloat? - What is the proper name for the situation I am facing so I can Google the answer next time?