问题在于你需要一个依赖量词,而Haskell目前缺乏这个功能。也就是说,
(x : A)(xs : Vec A (suc n)) → ...
这部分无法直接表达。你可能可以使用单例来解决问题,但这会变得非常丑陋和复杂。
我建议只需定义:
delete ∷ Eq a ⇒ a → Vec a (S n) → Maybe (Vec a n)
并且对此感到满意。我还会更改Vec
参数的顺序,以使提供应用程序、可遍历和其他实例成为可能。
实际上,不需要。为了定义delete
,只需要提供要删除的索引:
{-# LANGUAGE GADTs, DataKinds #-}
data Nat = Z | S Nat
data Index n where
IZ :: Index n
IS :: Index n -> Index (S n)
data Vec n a where
Nil :: Vec Z a
(:-) :: a -> Vec n a -> Vec (S n) a
delete :: Index n -> Vec (S n) a -> Vec n a
delete IZ (x :- xs) = xs
delete (IS n) (x :- (y :- xs)) = x :- delete n (y :- xs)
请注意,
x ∈ xs
仅仅是一个类型丰富的索引。
这里有一个使用singleton的解决方案:
{-# LANGUAGE GADTs, DataKinds, PolyKinds, KindSignatures, UndecidableInstances, TypeFamilies, RankNTypes, TypeOperators #-}
infixr 5 :-
data Nat = Z | S Nat
data family Sing (x :: a)
data instance Sing (b :: Bool) where
STrue :: Sing True
SFalse :: Sing False
data instance Sing (n :: Nat) where
SZ :: Sing Z
SS :: Sing n -> Sing (S n)
type family (:==) (x :: a) (y :: a) :: Bool
class SEq a where
(===) :: forall (x :: a) (y :: a). Sing x -> Sing y -> Sing (x :== y)
type instance Z :== Z = True
type instance S n :== Z = False
type instance Z :== S m = False
type instance S n :== S m = n :== m
instance SEq Nat where
SZ === SZ = STrue
SS n === SZ = SFalse
SZ === SS m = SFalse
SS n === SS m = n === m
data Vec xs a where
Nil :: Vec '[] a
(:-) :: Sing x -> Vec xs a -> Vec (x ': xs) a
type family If b x y where
If True x y = x
If False x y = y
type family Delete x xs where
Delete x '[] = '[]
Delete x (y ': xs) = If (x :== y) xs (y ': Delete x xs)
delete :: forall (x :: a) xs. SEq a => Sing x -> Vec xs a -> Vec (Delete x xs) a
delete x Nil = Nil
delete x (y :- xs) = case x === y of
STrue -> xs
SFalse -> y :- delete x xs
test :: Vec '[S Z, S (S (S Z)), Z] Nat
test = delete (SS (SS SZ)) (SS SZ :- SS (SS (SS SZ)) :- SS (SS SZ) :- SZ :- Nil)
在这里,我们通过元素的列表索引Vec
并将单例作为向量的元素存储。我们还定义了一个类型类SEq
,其中包含一个方法,接受两个单例并返回它们推广的值的相等或不相等的证明。接下来,我们定义了一种类型家族Delete
,它类似于列表的常规delete
,但是在类型级别上进行。最后,在实际的delete
中,我们对x === y
进行模式匹配,从而揭示x
是否等于y
,使得类型家族计算。
Either
,其中Left xs
表示未删除任何值的原始向量,而Right ...
表示已删除x
的新向量。 - chepner