我正在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与[Char]排成一行 (**)
,而类似整数与Bool一致 (***)
。
我想我知道问题出在哪里:一旦来自运营商类型的a
被确定为行Integer
中的(*)
,就无法更改。所以我的问题有两个:
为什么会出现此问题,前提是结果类型(Bool
)始终相同,无论运算符的参数类型如何?
可以做些什么来完成这项工作?
答案 0 :(得分:1)
类型签名a -> a -> Bool
表示必须存在某些类型a
op
具有该类型。但是您希望这适用于多个类型a
。你不能在Haskell&#39; 98中做到这一点。
如果你打开rank-2类型(或rank-N类型),那么你可以
evalComparison :: Exp -> (forall a. a -> a -> Bool) -> Exp -> Result Value
这表示无论您传入的op
是什么,都必须适用于多个类型a
。事实上,它说op
必须适用于所有可能的类型 a
。但那可能太多了。你想要的可能更接近
evalComparison :: Exp - &gt; (foral a.Od a =&gt; a - &gt; a - &gt; Bool) - &gt; Exp - &gt;结果值
这表示op
必须适用于实现a
的每个Ord
。
虽然坦白地说,你可以在你的case-expression中明确地调用compare
。让evalComparison
返回Ordering
,然后对其应用Ordering -> Result Value
。 Ordering
只是一种类型,它可以使事情变得更简单。