Implementing length-indexed vector using dependent types (cont.)
We’ve implemented a length-indexed vector manually in the previous post. How will it look like with singletons?
We no longer need to define SNat
manually. Also, by making Nat
derive Eq
, singletons makes SNat
an instance of TestEquality
. So we don’t need to write sameSNat
.
{-# LANGUAGE DataKinds,
EmptyCase,
GADTs,
InstanceSigs,
KindSignatures,
RankNTypes,
ScopedTypeVariables,
StandaloneDeriving,
StandaloneKindSignatures,
TemplateHaskell,
TypeApplications,
TypeFamilies,
TypeOperators,
UndecidableInstances
#-}
import Data.Kind (Type)
import Data.Singletons.Prelude (FlipSym1)
import Data.Singletons.Sigma (Sigma((:&:)))
import Data.Singletons.TH
import Data.Type.Equality ((:~:)(Refl), testEquality)
singletons [d|
data Nat = Z | S Nat deriving (Show, Eq)
|]
type Nat0 = 'Z
type Nat1 = 'S 'Z
type Nat2 = 'S Nat1
type Nat3 = 'S Nat2
The definition of Vec
is pretty much the same, but we can use Sigma
instead of our hand-written SomeVec
.
type Vec :: Nat -> Type -> Type
data Vec len a where
VCons :: a -> Vec len a -> Vec ('S len) a
VNil :: Vec 'Z a
deriving instance Show a => Show (Vec len a)
type SomeVec a = Sigma Nat (FlipSym1 (TyCon Vec) @@ a)
The usages of it are very simillar to what we saw in the previous post.
vec :: [a] -> SomeVec a
vec [] = SZ :&: VNil
vec (x:xs) = case vec xs of
sLen :&: v -> SS sLen :&: VCons x v
sum3 :: Monoid a => Vec Nat3 a -> a
sum3 (VCons a1 (VCons a2 (VCons a3 VNil))) = a1 a2 a3
trySum3 :: Monoid a => [a] -> Maybe a
trySum3 xs = case vec xs of
(SS (SS (SS SZ))) :&: v -> Just $ sum3 v
_ -> Nothing
vzip :: Vec len a -> Vec len b -> Vec len (a, b)
vzip VNil VNil = VNil
vzip (VCons x xs) (VCons y ys) = VCons (x, y) $ vzip xs ys
tryZip :: [a] -> [b] -> Maybe (SomeVec (a, b))
tryZip xs ys = case (vec xs, vec ys) of
(sLenX :&: vx, sLenY :&: vy) ->
case testEquality sLenX sLenY of
Just Refl -> Just (sLenX :&: vzip vx vy)
Nothing -> Nothing