Haskell 高阶函数中的多态性?

Haskell 高阶函数中的多态性?,haskell,types,polymorphism,higher-order-functions,Haskell,Types,Polymorphism,Higher Order Functions,我有一个代数数据类型,其中一些构造函数具有可比较的值,而一些构造函数不具有可比较的值。我编写了一些比较函数,它们的工作方式类似于标准的(==)和(/=)运算符,但对于没有意义的比较,它们返回Nothing: data Variant = IntValue Int | FloatValue Float | NoValue equal :: Variant -> Variant -> Maybe Bool equal (IntValu

我有一个代数数据类型,其中一些构造函数具有可比较的值,而一些构造函数不具有可比较的值。我编写了一些比较函数,它们的工作方式类似于标准的
(==)
(/=)
运算符,但对于没有意义的比较,它们返回
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
显然不能在
helper
的定义中同时绑定到
Int
Float
;GHC将其绑定到
Float
,然后抱怨处理
IntValue
的行上的类型不匹配


(==)
这样的函数在直接使用时是多态的;是否有方法将其传递给另一个函数并使其保持多态性?

是的,这是可能的,但仅限于:

所有a.的
都是关于它听起来像什么;
a
在括号内普遍量化,在括号外则超出范围。这意味着
f
参数必须在属于
Eq
实例的所有类型a上具有多态性,这正是您想要的

这里的扩展名为“rank2”,因为它允许在最外层范围内使用常规样式的多态性,以及此处示例中的多态参数。为了进一步嵌套,您需要扩展
RankNTypes
,它是相当自描述性的


另一方面,关于更高级别的多态类型——请记住,
forall
实际上是将变量绑定到类型的;事实上,你可以认为他们的行为很像羔羊。当您将这样一个函数应用于具有具体类型的对象时,参数的类型将隐式地由该用法的
forall
绑定。例如,如果您尝试使用一个值,该值的类型被该函数外部的所有
绑定;值的类型超出了范围,这使得很难做任何合理的事情(正如您可能想象的那样)。

Hmm,我感觉答案将涉及所有的
,实际上我几乎完全尝试了,但我缺少了您添加的一对括号。我不太明白为什么那些父母是必要的;不要使用它们。@Wyzard:这就是我添加编辑的原因。。。想象一下,
forall
实际上是一个函数,它接受了一个额外的参数(有点像是在幕后)。括号是作为
helper
(默认值)或
f
(您需要的)参数的类型之间的区别。在大括号中写入“类型参数”,在一种情况下,我们有
helper{a}(f::a->a->Bool)(IntValue x)(IntValue y)=Just(fxy)
,这是一个类型错误,因为
a
在外部范围内(在
Int
Float
的不同位置使用)。在另一个位置,我们有
helper f(IntValue x)(IntValue y)=Just(f{Int}xy)
,同样地,将
f
应用到另一个位置的
Float
,因此类型工作。好的,我想我现在明白了。谢谢
{-# LANGUAGE Rank2Types #-}

helper :: (forall a. (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