高阶函数内的多态性?

时间:2011-08-15 03:44:44

标签: haskell types polymorphism higher-order-functions

我有一个代数数据类型,其中一些构造函数具有可比较的值,而某些构造函数则没有。我编写了一些与标准(==)(/=)运算符类似的比较函数,但返回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的定义中同时绑定到Floathelper; GHC将其绑定到Float,然后抱怨处理IntValue的行上的类型不匹配。

(==)这样的函数在直接使用时是多态的;有没有办法将它传递给另一个函数,并保持多态?

1 个答案:

答案 0 :(得分:15)

是的,这是可能的,但只能使用language extensions

{-# 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

forall a.关于听起来是什么感觉; a在括号内被普遍量化,超出了它们之外的范围。这意味着f参数必须是Eq实例的所有类型的多态,这正是您想要的。

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

另外,关于更高级别的多态类型 - 请记住forall实际上是将变量绑定到某个类型;事实上,你可以把它们看作很像lambda的行为。当您将此类函数应用于具有特定类型的事物时,参数的类型将由forall隐式绑定以用于该用途。例如,如果您尝试使用其类型由该函数外部的内部forall绑定的值,则会出现这种情况;值的类型超出了范围,这使得很难做任何合理的事情(你可以想象)。