Haskell: Using the same operator on different types in a function
I'm writing a simple interpreter in Haskell. I have 3 possible variable types: bool
, int
and string
. To avoid repetition in evaluating comparisons, I've written a function that takes 2 expressions and an operator:
data Value = IntVal Integer
| StringVal String
| BoolVal Bool
| ...
evalComparison :: Ord a => Exp -> (a -> a -> Bool) -> Exp -> Result Value
evalComparison expr1 op expr2 = do
val1 <- evalExp expr1
val2 <- evalExp expr2
return $ BoolVal $
case (val1, val2) of
(IntVal i1, IntVal i2) -> op i1 i2 (*)
(StringVal s1, StringVal s2) -> op s1 s2 (**)
(BoolVal b1, BoolVal b2) -> op b1 b2 (***)
otherwise -> error "Expected values of the same type!"
It's intended usage is, for example:
evalExp :: Exp -> Result Value
...
evalExp (ELessThen e1 e2) = evalComparison e1 (<) e2
(And so on for other comparison operators).
The problem is - it doesn't work. GHC says it couldn't match type Integer with [Char] in line (**)
and similarly Integer with Bool in line (***)
.
I think I know where the problem lies: once the a
from the operator's type is determined as Integer
in line (*)
, it cannot be changed. So my question is twofold:
Why does this problem arise, provided that the result type ( Bool
) is always the same regardless of the operator's arguments' types?
What can be done to make this work?
The type signature a -> a -> Bool
says that there must exist some type a
for which op
has that type. But you want this to work for more than one type a
. You can't do that in Haskell '98.
If you turn on rank-2 types (or rank-N types), then you can do
evalComparison :: Exp -> (forall a. a -> a -> Bool) -> Exp -> Result Value
This says that whatever you pass in as op
has to work for multiple types a
. In fact, it says that op
has to work for all possible types a
. But that's probably too much. What you want is probably closer to
evalComparison :: Exp -> (forall a. Ord a => a -> a -> Bool) -> Exp -> Result Value
This says that op
has to work for every possible a
that implements Ord
.
Although frankly at that point, you might as well just call compare
explicitly in your case-expression. Have evalComparison
return an Ordering
, and then apply a Ordering -> Result Value
to that. Ordering
is only one type, which should make things simpler.
上一篇: 在Haskell中打印空列表