1

我有一个类型

data Value = Int Integer
           | Float Double
           | Complex (Complex Double)
           | ... (other, non-numeric types)

具有关联错误类型

data ValueError = TypeMismatch Value | ... (other constructors)

type ThrowsError = Either ValueError

我想在该类型上实现通用二进制操作,自动强制转换为层次结构中的最高类型,并在其中一个操作数不是数字类型(即函数)的情况下发出错误信号

binaryOp :: Num a => (a -> a -> a) -> Value -> Value -> ThrowsError Value 

这样我就可以写,例如,

(binaryOp (+)) (Int 1) (Int 1)      ==> Right (Int 2)
(binaryOp (+)) (Int 1) (Float 1.0)  ==> Right (Float 2.0)
(binaryOp (+)) (Int 1) (String "1") ==> Left (TypeMismatch (String "1"))

有没有一种简单的方法可以做到这一点?我的第一个想法是定义类似

data NumType = IntType | FloatType | ComplexType

连同功能

typeOf :: Value -> NumType
typeOf (Int _) = IntType
...

promote :: Value -> Value
promote (Int n)   = Float (fromInteger n)
promote (Float n) = Complex (n :+ 0)

但我很难让它发挥作用。有什么建议吗?


多一点上下文。我正在编写一个 Scheme 解释器,我想实现 Scheme numeric tower

事实上,我想要实现比我解释的稍微复杂一些的东西,因为我想要一些适用于任意数量的参数的东西,沿着

binaryOp :: Num a => (a -> a -> a) -> [Value] -> ThrowsError Value

这将用 实现foldl1,但我觉得如果我能解决更简单的问题,那么我将能够解决这个更复杂的问题。

4

1 回答 1

2

像这样的东西:

data NumType = IntType | FloatType | ComplexType | NotANumType
  deriving (Eq, Ord)

binaryOp :: (forall a. Num a => a -> a -> a) -> Number -> Number -> ThrowsError Number
binaryOp op x y
   = case typeOf x `max` typeOf y of
          ComplexType -> Complex (asComplex x `op` asComplex y)
          ...

我认为您将需要启用 Rank2Types 扩展名(插入{-# LANGUAGE Rank2Types #-}源文件顶部)以正确说明 的类型binaryOp,我不确定我的语法是否正确......

的类型binaryOp比你想象的要复杂,因为它binaryOp选择a调用的时候是什么op。你写的内容会让binaryOp's caller 选择什么a是你想要的,这不是你想要的。

于 2012-04-18T20:10:23.067 回答