Haskell:在函数的不同类型中使用相同的运算符

我正在哈斯克尔写一个简单的解释器。 我有3种可能的变量类型: boolintstring 。 为了避免重复评估比较,我写了一个函数,它带有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 ValueOrdering只是一种类型,应该使事情变得更简单。

    链接地址: http://www.djcxy.com/p/33155.html

    上一篇: Haskell: Using the same operator on different types in a function

    下一篇: How do I cast from Integer to Fractional