部分应用型家庭

时间:2017-03-01 11:18:28

标签: haskell type-constraints type-families

这是this question的后续问题,其中允许部分应用(~)类型运算符的答案。

考虑以下(愚蠢)类型系列:

type family SillyT a b :: Constraint

data D (c :: * -> Constraint) where
  D :: Proxy c -> D c

然后这样的事情无效:

D (Proxy :: (Proxy (SillyT Int)))

但是,如果我将SillyT包装在类似的类中:

class SillyT a b => SillyC a b
instance SillyT a b => SillyC a b

然后我可以这样做:

D (Proxy :: (Proxy (SillyC Int)))

一切正常。然而,在类中包装似乎有点愚蠢和重复,但这是部分应用类型系列的唯一方法吗?

1 个答案:

答案 0 :(得分:1)

您提到的这两个定义: 从{Haskell的类型检查器的角度来看,type family SillyT a b :: Constraintclass ... => SillyC a b非常非常不同。

如果您看到前者,您唯一知道的是在应用两个类型变量后得到Constraint,但是您没有告诉GHC在仅应用一个后会发生什么。您应该能够将SillyT定义为type family SillyT a :: * -> Constraint,然后更多地告诉GHC。

考虑一下构造函数变量。如果你告诉GHC某事是Functor,GHC知道它的“形状”是f a,其中a是某些f的最后一个参数。您必须告诉GHC它可以以相同的方式分析SillyT的结果,因此GHC可以肯定,它始终可以访问查看结果类型的最后一个参数。

另一方面,

类型类在这里更具表现力。它们是一种类型,而不是类型转换(如类型族),GHC可以对它们的变量进行“模式匹配”,因此您不会遇到上述限制。此外,类型类允许您定义重叠实例,这在使用类型族时是不可能的。但是,函数依赖性不如类型族灵活,因为除非GHC支持不可预测的上下文,否则使用类型deps很难摆脱不必要的自由类型变量。

我希望它澄清一些事情:)