我有一个代数数据类型,其中一些构造函数持有可比较的值,而另一些构造函数则不持有。我编写了一些比较函数,它们的工作方式类似于标准的(==)
和(/=)
运算符,但对于不合理的比较返回Nothing
:
我有一个代数数据类型,其中一些构造函数持有可比较的值,而另一些构造函数则不持有。我编写了一些比较函数,它们的工作方式类似于标准的(==)
和(/=)
运算符,但对于不合理的比较返回Nothing
:
data Variant = IntValue Int
| FloatValue Float
| NoValue
equal :: Variant -> Variant -> Maybe Bool
equal (IntValue a) (IntValue b) = Just (a == b)
equal (FloatValue a) (FloatValue b) = Just (a == b)
equal _ _ = Nothing
unequal :: Variant -> Variant -> Maybe Bool
unequal (IntValue a) (IntValue b) = Just (a /= b)
unequal (FloatValue a) (FloatValue b) = Just (a /= b)
unequal _ _ = Nothing
那行得通,但是重复性很难处理——尤其是因为我实际上有更多的 Variant
构造函数和比较函数。
我想将这个重复的部分提炼到一个帮助函数中,并使其参数化比较函数:
helper :: (Eq a) => (a -> a -> Bool) -> Variant -> Variant -> Maybe Bool
helper f (IntValue a) (IntValue b) = Just (f a b)
helper f (FloatValue a) (FloatValue b) = Just (f a b)
helper _ _ _ = Nothing
equal' :: Variant -> Variant -> Maybe Bool
equal' = helper (==)
unequal' :: Variant -> Variant -> Maybe Bool
unequal' = helper (/=)
但这并不起作用,因为类型变量 a
显然不能同时绑定到 Int
和 Float
上,在 helper
的定义中; GHC将其绑定到 Float
,然后在处理 IntValue
的行上报告类型不匹配错误。
像 (==)
这样的函数在直接使用时是多态的;有没有办法将它传递给另一个函数并保持其多态性?
forall
实际上是一个带有额外参数的函数(在幕后也是如此)。括号的区别在于类型是作为helper
的参数(默认情况)还是作为f
的参数(你想要的)。 - C. A. McCannhelper {a} (f :: a -> a -> Bool) (IntValue x) (IntValue y) = Just (f x y)
,但这是一个类型错误,因为a
在外部作用域中(在不同的位置上使用了Int
和Float
)。在另一个例子中,我们有helper f (IntValue x) (IntValue y) = Just (f {Int} x y)
,并在另一个位置将f
应用于Float
,因此类型匹配。 - C. A. McCann