将类型族约束限制为“某对”

时间:2016-09-21 02:19:23

标签: haskell type-constraints type-families

考虑以下内容,我试图说“a是一对”:

type family F t a :: Constraint
type instance F Int a = (a ~ (a1, a2))

这不起作用,因为a1a2都不在范围内,但有什么方法可以表达这一点吗?

当然在函数签名中我可以写(a ~ (a1, a2))之类的约束,即使其他地方没有提到a1a2,但是我需要把它放在一个实例函数签名中,当然是由全班决定的。在这种情况下,a不是实例本身的参数,只是实例函数,就像Functor只有f作为类参数,而不是a和{ {1}},所以我不能在实例子句中添加额外的约束。

1 个答案:

答案 0 :(得分:8)

是的!你可以这样做!

强大,有点建立的版本

首先,有几个有用的类型系列:

type family Fst a where
  Fst (x,y) = x

type family Snd a where
  Snd (x,y) = y

现在你要追求的那个:

type IsPair a = a ~ (Fst a, Snd a)

这是一个测试:

type family Con a where
  Con (f x y) = f

test :: IsPair a => proxy a -> Con a :~: (,)
test _ = Refl

更简单的一个结果是测试更强大的属性:

 test1 :: IsPair a => a -> Fst a
 test1 = fst

只是为了确保它应该满足:

data Dict c where
  Dict :: c => Dict c

test2 :: Dict (IsPair (a, b))
test2 = Dict

您当然可以使用它来定义F

type family F t a
type instance F Int a = IsPair a

更简单,但功能更弱

type family Con a where
  Con (f x y) = f
type IsPair a = Con a ~ (,)

与第一种方法相比,这个方法的问题在于它实际上并没有赢得a ~ (Fst a, Snd a)的光荣知识。所以它发表了一个声明,但你不能用它来做很多事情。

一点概括

为什么只配对?如果你将PolyKinds投入混合中,你会变得非常笼统:

type family Arg1 a where Arg1 (f x) = x
type family Arg2 a where Arg2 (f y x) = y
type family Arg3 a where Arg3 (f z y x) = z
type family Arg4 a where Arg4 (f w z y x) = w

type family IsF (f :: k) (a :: *) :: Constraint
type instance IsF f a = a ~ f (Arg1 a)
type instance IsF f a = a ~ f (Arg2 a) (Arg1 a)
type instance IsF f a = a ~ f (Arg3 a) (Arg2 a) (Arg1 a)
type instance IsF f a = a ~ f (Arg4 a) (Arg3 a) (Arg2 a) (Arg1 a)

您也可以在没有PolyKinds的情况下执行此操作,但之后需要IsF1IsF2等。

有了这个,

type IsPair a = IsF (,) a
type IsMaybe a = IsF Maybe a
...

测试一般化(这些东西仅适用于GHC 7.10或更高版本;聚合物在此之前太脆弱了。)

data Dict c where
  Dict :: c => Dict c

test1 :: Dict (IsF f (f a))
test1 = Dict

test2 :: Dict (IsF f (f a b))
test2 = Dict

test3 :: Dict (IsF f (f a b c))
test3 = Dict

test4 :: Dict (IsF f (f a b c d))
test4 = Dict