考虑以下内容,我试图说“a
是一对”:
type family F t a :: Constraint
type instance F Int a = (a ~ (a1, a2))
这不起作用,因为a1
和a2
都不在范围内,但有什么方法可以表达这一点吗?
当然在函数签名中我可以写(a ~ (a1, a2))
之类的约束,即使其他地方没有提到a1
和a2
,但是我需要把它放在一个实例函数签名中,当然是由全班决定的。在这种情况下,a
不是实例本身的参数,只是实例函数,就像Functor
只有f
作为类参数,而不是a
和{ {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
的情况下执行此操作,但之后需要IsF1
,IsF2
等。
有了这个,
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