定义:
type family (xs :: [*]) ++ (ys :: [*]) where
'[] ++ ys = ys
(x ': xs) ++ ys = x ': (xs ++ ys)
我有一个GADT,有点像
data Foo :: [*] -> * -> * where
Foo0 :: a -> Foo '[] a
Foo1 :: Foo '[a] a
Foo2 :: Foo vs a -> Foo us a -> Foo (vs ++ us) a
我想做一些像
这样的事情test :: Foo '[] Int -> Int
test (Foo0 x) = x
test (Foo2 x y) = test x + test y
但我无法在test
或x
上使用y
,因为必须证明x ~ Foo '[] Int
和y ~ Foo '[] Int
。但我想说,vs ++ us ~ '[]
意味着vs
和us
的个人x
和y
必然{{1} }}
有没有办法用类型系列做到这一点,或者可能切换到带有fundeps的多参数类型方法?
谢谢!
答案 0 :(得分:6)
“绿色粘液”的存在 - 返回类型中定义的函数 建设者 - 是一个危险的信号。
最简单的解决方法是概括test
然后实例化:
gtest :: Foo xs Int -> Int
gtest (Foo0 x) = x
gtest (Foo2 x y) = gtest x + gtest y
test :: Foo '[] Int -> Int
test = gtest
答案 1 :(得分:4)
您可以添加两个类型系列作为++
的反转,并且不失一般性,将它们作为约束添加到Foo2构造函数中。通过这些反向型家庭,GHC将能够准确地推断出您从中得到的内容。
以下是CutX
和CutY
的示例实现,r ~ a ++ b
< => a ~ CutY r b
< => b ~ CutX r a
。
type family (xs :: [*]) ++ (ys :: [*]) where
'[] ++ ys = ys
(x ': xs) ++ ys = x ': (xs ++ ys)
type family CutX (rs :: [*]) (xs :: [*]) where
CutX '[] xs = '[]
CutX rs '[] = rs
CutX (r ': rs) (x ': xs) = CutX rs xs
type family ZipWithConst (xs :: [*]) (ys :: [*]) where
ZipWithConst '[] ys = '[]
ZipWithConst xs '[] = '[]
ZipWithConst (x ': xs) (y ': ys) = y ': ZipWithConst xs ys
type CutY rs ys = ZipWithConst rs (CutX rs ys)
data Foo :: [*] -> * -> * where
Foo0 :: a -> Foo '[] a
Foo1 :: Foo '[a] a
Foo2 :: (rs ~ (vs ++ us), us ~ CutX rs vs, vs ~ CutY rs us) => Foo vs a -> Foo us a -> Foo rs a