packages feed

debruijn-0.1: src/DeBruijn/Internal/Add.hs

{-# LANGUAGE Unsafe #-}
module DeBruijn.Internal.Add (
    Add (AZ, AS, UnsafeAdd),
    addToInt,
    addToSize,
    adding,
    -- * Lemmas
    rzeroAdd,
    unrzeroAdd,
    lzeroAdd,
    unlzeroAdd,
    rsuccAdd,
    unrsuccAdd,
    lsuccAdd,
    unlsuccAdd,
    swapAdd,
    unswapAdd,
) where


import Data.Coerce        (coerce)
import Data.GADT.Show     (GShow (..))
import Data.Kind          (Type)
import Data.Some          (Some (..))
import Data.Type.Equality ((:~:) (Refl))
import Unsafe.Coerce      (unsafeCoerce)

import DeBruijn.Ctx
import DeBruijn.Internal.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

newtype Add n m p = UnsafeAdd { _addToInt :: Int }

addToInt :: Add n m p -> Int
addToInt = _addToInt

addToSize :: Add n m p -> Size n
addToSize (UnsafeAdd n) = UnsafeSize n

instance Show (Add n m p) where
    showsPrec d a = showsPrec d (addToInt a)

instance GShow (Add n m) where
    gshowsPrec = showsPrec

type ViewAdd :: Ctx -> Ctx -> Ctx -> Type
type role ViewAdd nominal nominal nominal
data ViewAdd n m p where
    AZ' :: ViewAdd EmptyCtx ctx ctx
    AS' :: !(Add n ctx ctx') -> ViewAdd (S n) ctx (S ctx')

viewAdd :: Add n m p -> ViewAdd n m p
viewAdd (UnsafeAdd n)
    | n <= 0    = unsafeCoerce AZ'
    | otherwise = unsafeCoerce (AS' (UnsafeAdd (n - 1)))

pattern AZ :: () => (n ~ EmptyCtx, m ~ p) => Add n m p
pattern AZ <- (viewAdd -> AZ')
  where AZ = UnsafeAdd 0

pattern AS :: () => (n ~ S n', p ~ S p') => Add n' m p' -> Add n m p
pattern AS a <- (viewAdd -> AS' a)
  where AS a = UnsafeAdd (_addToInt a + 1)

{-# COMPLETE AZ, AS #-}

-- | Add @n@ to some context @ctx@.
--
-- >>> adding (SS (SS SZ))
-- Some 2
--
adding :: Size n -> Some (Add n ctx)
adding (UnsafeSize n) = Some (UnsafeAdd n)

-------------------------------------------------------------------------------
-- Lemmas: zero
-------------------------------------------------------------------------------

-- | @n + 0 ≡ 0@
rzeroAdd :: Size n -> Add n EmptyCtx n
rzeroAdd (UnsafeSize n) = UnsafeAdd n

-- | @n + 0 ≡ m → n ≡ m@
unrzeroAdd :: Add n EmptyCtx m -> n :~: m
unrzeroAdd (UnsafeAdd !_) = unsafeCoerce 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 = coerce

-- | @n + S m ≡ S p → n + m ≡ p@
unrsuccAdd :: Add n (S m) (S p) -> Add n m p
unrsuccAdd = coerce

-- | @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 (UnsafeAdd n) = UnsafeAdd (n + 1)

-- | @S n + m ≡ p → n + S m ≡ p@
unswapAdd :: Add (S n) m p -> Add n (S m) p
unswapAdd (UnsafeAdd n) = UnsafeAdd (n - 1)