将类型族约束约束为 "some pair"

Constrain a type family constraint to be "some pair"

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

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

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

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

是的!你可以做到!

强壮的,有点内置的版本

首先,几个有用的类型族:

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 或更高版本;在此之前 polykinds 太不稳定了)。

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