将多态函数应用于两种不同类型的输入

时间:2019-01-10 22:43:54

标签: haskell types parametric-polymorphism

考虑此功能:

doToBoth f x y = (f x, f y)

在简单情况下,它可以正常工作:

doToBoth (2 *) 10 15 == (20, 30)
doToBoth head [1,2] [3,4,5] == (1, 3)

然后我尝试了这些:

doToBoth head [1,10,100] "apple"
doToBoth pred 2 'b'

我希望它们都导致(1, 'a'),但是它们只会导致类型错误。问题在于doToBoth的推断类型不够多态:

doToBoth :: (a1 -> a2) -> a1 -> a1 -> (a2, a2)

看到这一点,我尝试添加一个显式类型签名来对其进行修复:

doToBoth :: (t ~ (i1 -> o1), t ~ (i2 -> o2)) => t -> i1 -> i2 -> (o1, o2)

此类型签名已被接受,但不能解决问题,检查:t doToBoth所发生的情况后发现,该类型签名最终具有与原始推断出的类型相同的类型:

doToBoth :: (i2 -> o2) -> i2 -> i2 -> (o2, o2)

编写类型签名以使此功能按我想要的方式工作的正确方法是什么?

1 个答案:

答案 0 :(得分:4)

接受多态参数会使函数等级2多态。 GHC具有an extension for that,但只有在您可以某种方式量化参数必须支持的类型时,才能使用它-使用类型构造函数或-classes。例如,对于列表,您可以编写

{-# LANGUAGE Rank2Types, UnicodeSyntax #-}
doToBoth_listelem :: (∀ x . [x] -> x) -> [a] -> [b] -> (a,b)
doToBoth_listelem f x y = (f x, f y)
> doToBoth_listelem head [1,10,100] "apple"
(1,'a')

这也适用于pred示例,并且更有用。在这种情况下,您需要量化受Enum类约束的参数:

doToBoth_enum :: (Enum a, Enum b)
      => (∀ x . Enum x => x -> x) -> a -> b -> (a,b)
doToBoth_enum f x y = (f x, f y)
> doToBoth_enum pred 2 'b'
(1,'a')

编写它,使其自动适用于参数可能需要的任何此类约束,我认为这是不可能的。可能可以用一些聪明的类型族和约束类型来近似,但是我怀疑它最终是否会实用。