我有一个类型
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
实现,但我觉得如果我能解决更简单的问题,那么我将能够解决这个更复杂的问题。
答案 0 :(得分: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
的来电者选择a
是什么,这不是你想要的。