{-# LANGUAGE Safe #-} module DeBruijn.Add ( Add (AZ, AS), addToInt, addToSize, adding, -- * Lemmas rzeroAdd, unrzeroAdd, lzeroAdd, unlzeroAdd, rsuccAdd, unrsuccAdd, lsuccAdd, unlsuccAdd, swapAdd, unswapAdd, ) where import Data.GADT.Show (GShow (..)) import Data.Kind (Type) import Data.Some (Some (..)) import Data.Type.Equality ((:~:) (..)) import DeBruijn.Ctx import DeBruijn.Size -- $setup -- >>> import DeBruijn -- | @'Add' n m p@ is an evidence that @n + m = p@. -- -- Useful when you have an arity @n@ thing and need to extend a context @ctx@ with: @'Add' n ctx ctx'@. -- -- Using a type representing a graph of a type function is often more convenient than defining type-family to begin with. -- type Add :: Ctx -> Ctx -> Ctx -> Type type role Add nominal nominal nominal data Add n m p where AZ :: Add EmptyCtx ctx ctx AS :: !(Add n ctx ctx') -> Add (S n) ctx (S ctx') addToInt :: Add n m p -> Int addToInt = go 0 where go :: Int -> Add n m p -> Int go !n AZ = n go !n (AS a) = go (n + 1) a addToSize :: Add n m p -> Size n addToSize AZ = SZ addToSize (AS a) = SS (addToSize a) instance Show (Add n m p) where showsPrec d a = showsPrec d (addToInt a) instance GShow (Add n m) where gshowsPrec = showsPrec -- | Add @n@ to some context @ctx@. -- -- >>> adding (SS (SS SZ)) -- Some 2 -- adding :: Size n -> Some (Add n ctx) adding SZ = Some AZ adding (SS s) = case adding s of Some a -> Some (AS a) ------------------------------------------------------------------------------- -- Lemmas: zero ------------------------------------------------------------------------------- -- | @n + 0 ≡ 0@ rzeroAdd :: Size n -> Add n EmptyCtx n rzeroAdd SZ = AZ rzeroAdd (SS s) = AS (rzeroAdd s) -- | @n + 0 ≡ m → n ≡ m@ unrzeroAdd :: Add n EmptyCtx m -> n :~: m unrzeroAdd AZ = Refl unrzeroAdd (AS a) = case unrzeroAdd a of Refl -> Refl -- | @0 + n ≡ 0@ lzeroAdd :: Size n -> Add EmptyCtx n n lzeroAdd _ = AZ -- | @0 + n ≡ m → n ≡ m@ unlzeroAdd :: Add EmptyCtx n m -> n :~: m unlzeroAdd AZ = Refl ------------------------------------------------------------------------------- -- Lemmas: succ ------------------------------------------------------------------------------- -- | @n + m ≡ p → n + S m ≡ S p@ rsuccAdd :: Add n m p -> Add n (S m) (S p) rsuccAdd AZ = AZ rsuccAdd (AS a) = AS $ rsuccAdd a -- | @n + S m ≡ S p → n + m ≡ p@ unrsuccAdd :: Add n (S m) (S p) -> Add n m p unrsuccAdd AZ = AZ unrsuccAdd (AS a) = swapAdd a -- | @n + m ≡ p → S n + m ≡ S p@ lsuccAdd :: Add n m p -> Add (S n) m (S p) lsuccAdd = AS -- | @S n + m ≡ S p → n + m ≡ p@ unlsuccAdd :: Add (S n) m (S p) -> Add n m p unlsuccAdd (AS a)= a ------------------------------------------------------------------------------- -- Lemmas: swap ------------------------------------------------------------------------------- -- | @n + S m ≡ p → S n + m ≡ p@ swapAdd :: Add n (S m) p -> Add (S n) m p swapAdd AZ = AS AZ swapAdd (AS a) = AS $ swapAdd a -- | @S n + m ≡ p → n + S m ≡ p@ unswapAdd :: Add (S n) m p -> Add n (S m) p unswapAdd (AS a) = rsuccAdd a