我想知道是否有更深层次的原因我们不能抽象出类型类(或者我们可以吗?).
例如,当我们有
fzip :: (forall a.[a] -> [a]) -> [b] -> [c] -> [(b,c)] fzip f xs ys = zip (f xs) (f ys)
那我们可以说
fzip (drop 42) [1..100] ['a'..'z'] fzip reverse [1..100] ['a'..'z']
等等.但我们做不到
fzip (map succ) [1..100] ['a'..'z']
我们可以修复:
ezip :: (Enum b, Enum c) => (forall a.Enum a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] ezip f xs ys = zip (f xs) (f ys)
同样我们可以解决
fzip (map (5*)) [1..100] [1.5, 2.3, 4.7]
同
nzip :: (Num b, Num c) => (forall a.Num a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] nzip f xs ys = zip (f xs) (f ys)
然而,不尴尬,我们不能归入ezip
和nzip
喜欢的东西:
gzip :: (g b, g c) => (forall a. g a => [a] -> [a]) -> [b] -> [c] -> [(b,c)]
虽然代码是绝对相同的,但直到类的名称?或者我们能以某种方式?
有趣的是,当实例只是包含函数的记录时,这很容易实现.
你几乎可以这样做ConstraintKinds
:
{-# LANGUAGE ConstraintKinds, RankNTypes #-} import Data.Proxy gzip :: (g b, g c) => Proxy g -> (forall a . g a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] gzip _ f xs ys = zip (f xs) (f ys) test1 = gzip (Proxy :: Proxy Enum) (map succ) [1 .. 100] ['a' .. 'z'] test2 = gzip (Proxy :: Proxy Num) (map (5*)) [1 .. 100] [1.5, 2.3, 4.7]
主要区别在于您需要Proxy
参数,因为GHC无法在g
没有帮助的情况下推断出正确的实例化.
Proxy
为约束添加参数:
{-# LANGUAGE PartialTypeSignatures #-} import Data.Proxy gzip :: (g b, g c) => Proxy g -> (forall a. g a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] gzip _ f bs cs = zip (f bs) (f cs) > gzip (Proxy :: _ Enum) [0, 1] "ab" [(1,'b'),(2,'c')]
GHC认为约束参数仅出现在不明确的约束中,因此我们需要在代理中明确记录它们.