packages feed

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

{-# LANGUAGE Unsafe #-}
-- | de Bruijn levels for well-scoped terms.
module DeBruijn.Internal.Lvl (
    -- * Levels
    Lvl (UnsafeLvl),
    lvlToIdx,
    idxToLvl,
    lvlZ,
    sinkLvl,
    Sinkable (..),
    sink,
    mapSink,
    sinkSize,
    mapSinkSize,
    sinkAdd,
    mapSinkAdd,
) where

import Data.Coerce   (coerce)
import Data.Kind     (Constraint, Type)
import Data.Proxy    (Proxy (..))
import Unsafe.Coerce (unsafeCoerce)

import DeBruijn.Add
import DeBruijn.Ctx
import DeBruijn.Internal.Idx
import DeBruijn.Internal.Size
import DeBruijn.Lte

-- $setup
-- >>> import DeBruijn
-- >>> import DeBruijn.Lte

-------------------------------------------------------------------------------
-- de Bruijn levels
-------------------------------------------------------------------------------

-- | de Bruijn levels.
type Lvl :: Ctx -> Type
type role Lvl nominal
newtype Lvl ctx = UnsafeLvl { _unLvl :: Int }
  deriving (Eq, Ord)

-------------------------------------------------------------------------------
-- Instances
-------------------------------------------------------------------------------

instance Show (Lvl ctx) where
    showsPrec d (UnsafeLvl i) = showsPrec d i

-------------------------------------------------------------------------------
-- Helpers
-------------------------------------------------------------------------------

-- | Convert level to index.
--
-- >>> lvlToIdx S2 (lvlZ S1)
-- 0
--
lvlToIdx :: Size ctx -> Lvl ctx -> Idx ctx
lvlToIdx (UnsafeSize ctx) (UnsafeLvl lvl) = UnsafeIdx (ctx - lvl - 1)

idxToLvl :: Size ctx -> Idx ctx -> Lvl ctx
idxToLvl (UnsafeSize ctx) (UnsafeIdx idx) = UnsafeLvl (ctx - idx - 1)

-- | Last level.
--
-- >>> lvlZ S1
-- 1
--
-- >>> lvlZ S5
-- 5
--
lvlZ :: Size ctx -> Lvl (S ctx)
lvlZ (UnsafeSize s) = UnsafeLvl s

-- | Sink 'Lvl' into a larger context.
--
-- >>> sinkLvl (lvlZ S3)
-- 3
--
-- >>> sink (lvlZ S3)
-- 3
--
-- >>> mapLvl (LS LZ) (lvlZ S3)
-- 3
--
sinkLvl :: Lvl n -> Lvl (S n)
sinkLvl = coerce

-------------------------------------------------------------------------------
-- Sinkable
-------------------------------------------------------------------------------

-- | Sinkable terms can be weakened (sunk) cheaply.
type Sinkable :: (Ctx -> Type) -> Constraint
class Sinkable t where
    mapLvl :: Lte ctx ctx' -> t ctx -> t ctx'

instance Sinkable Lvl where mapLvl _ = coerce
instance Sinkable Proxy where mapLvl _ = coerce

-- | Sink term.
sink :: Sinkable t => t ctx -> t (S ctx)
sink = unsafeCoerce

-- | Sink term from empty context to a context of given size.
sinkSize :: Sinkable t => Size ctx -> t EmptyCtx -> t ctx
sinkSize _ = unsafeCoerce

-- | Essentially @'fmap' 'sink'@
mapSink :: (Functor f, Sinkable t) => f (t ctx) -> f (t (S ctx))
mapSink = unsafeCoerce

-- | Essentially @'fmap' . 'sinkSize'@
mapSinkSize :: (Functor f, Sinkable t) => Size ctx -> f (t EmptyCtx) -> f (t ctx)
mapSinkSize _ = unsafeCoerce

sinkAdd :: Sinkable t => Add n ctx ctx' -> t ctx -> t ctx'
sinkAdd _ = unsafeCoerce

mapSinkAdd :: (Functor f, Sinkable t) => Add n ctx ctx' -> f (t ctx) -> f (t ctx')
mapSinkAdd _ = unsafeCoerce