高阶函数内的多态性?

Wyz*_*a-- 16 polymorphism haskell types 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
Run Code Online (Sandbox Code Playgroud)

这是有效的,但重复是不实用的 - 特别是因为我实际上有更多的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 (/=)
Run Code Online (Sandbox Code Playgroud)

但这不起作用,因为类型变量a显然不能同时绑定到两者Int并且Float同时定义helper; GHC绑定它Float,然后抱怨处理线路上的类型不匹配IntValue.

(==)直接使用时,函数类似于多态; 有没有办法将它传递给另一个函数,并保持多态?

C. *_*ann 15

是的,这是可能的,但仅限于语言扩展:

{-# 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
Run Code Online (Sandbox Code Playgroud)

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

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

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