我想知道是否有更深层次的原因我们无法抽象出类型类(或者我们可以吗?)。
例如,当我们有
时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)]
尽管代码是绝对相同的,但是这个类的名称是什么? 或者我们能以某种方式吗?
有趣的是,当实例只是包含函数的记录时,这很容易实现。
答案 0 :(得分:11)
您可以几乎使用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
的正确实例化。
答案 1 :(得分:8)
为约束添加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认为约束参数只出现在不明确的约束中,因此我们需要在代理中明确记录它们。