Haskell:在函数的不同类型中使用相同的运算符
我正在哈斯克尔写一个简单的解释器。 我有3种可能的变量类型: bool
, int
和string
。 为了避免重复评估比较,我写了一个函数,它带有2个表达式和一个运算符:
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!"
它的用途是,例如:
evalExp :: Exp -> Result Value
...
evalExp (ELessThen e1 e2) = evalComparison e1 (<) e2
(对于其他比较运算符等)。
问题是 - 它不起作用。 GHC表示无法在行(**)
中将Integer类型的Integer与[Char]匹配,并且在行(***)
与Bool类似。
我想我知道问题出在哪里:一旦来自操作符类型的a
在行(*)
被确定为Integer
,它就不能被改变。 所以我的问题是双重的:
为什么会出现这个问题,前提是结果类型( Bool
)总是相同,而不管操作符的参数类型如何?
可以做些什么来完成这项工作?
类型签名a -> a -> Bool
表示必须存在某种类型a
op
,该类型具有该类型。 但是你希望这可以适用于多种类型a
。 你不能在Haskell '98中做到这一点。
如果开启rank-2类型(或rank-N类型),则可以
evalComparison :: Exp -> (forall a. a -> a -> Bool) -> Exp -> Result Value
这就是说,无论你作为op
传入什么,都必须为多种类型a
。 事实上,它说op
必须适用于所有可能的类型a
。 但这可能太多了。 你想要的可能更接近
evalComparison :: Exp - >(forall a。Ord a => a - > a - > Bool) - > Exp - > Result Value
这说, op
必须为实现Ord
每一个可能a
工作。
尽管坦率地说,在这一点上,你可能只是在你的case-expression中明确地调用compare
。 让evalComparison
返回一个Ordering
,然后将Ordering -> Result Value
应用于该Ordering -> Result Value
。 Ordering
只是一种类型,应该使事情变得更简单。
上一篇: Haskell: Using the same operator on different types in a function