naturals

natural numbers in Haskell
git clone git://git.rr3.xyz/naturals
Log | Files | Refs

commit 0896fd2d14acf5c75597c778b2685941313c2112
parent 3da4f50ce0b62a10d2c8c6d0216350639b45d1ec
Author: Robert Russell <robert@rr3.xyz>
Date:   Sun, 29 Jun 2025 17:27:57 -0700

Rename, clean up, extend, and improve

Diffstat:
ATODO | 2++
Msrc/Snats/Nat.hs | 64+++++++++++++++++++++++++---------------------------------------
Asrc/Snats/Natural.hs | 27+++++++++++++++++++++++++++
Asrc/Snats/Peano.hs | 7+++++++
Asrc/Snats/Peano/Alias.hs | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Snats/Peano/Prim.hs | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Snats/Snat.hs | 48++++++------------------------------------------
Asrc/Snats/Snat/Alias.hs | 34++++++++++++++++++++++++++++++++++
Asrc/Snats/Snat/Prim.hs | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Snats/Snat/Unsafe.hs | 35+++++++++++++++++++++++++++++++++++
Msrc/Snats/Snatural.hs | 46++++++----------------------------------------
Asrc/Snats/Snatural/Alias.hs | 34++++++++++++++++++++++++++++++++++
Asrc/Snats/Snatural/Prim.hs | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Snats/Snatural/Unsafe.hs | 30++++++++++++++++++++++++++++++
Asrc/Snats/Speano.hs | 9+++++++++
Asrc/Snats/Speano/Alias.hs | 34++++++++++++++++++++++++++++++++++
Asrc/Snats/Speano/Prim.hs | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 files changed, 535 insertions(+), 121 deletions(-)

diff --git a/TODO b/TODO @@ -0,0 +1,2 @@ +"Prim" does not contain primitives in the case of Snat and Snatural. +Rename these files to something more appropriate. diff --git a/src/Snats/Nat.hs b/src/Snats/Nat.hs @@ -1,48 +1,34 @@ module Snats.Nat ( - Nat (..), - CaseDecision (..), - Pred, - Add, + Nat, + ToNat (..), + FromNat (..), ) where -import Control.Exception +-- | Word-sized natural numbers. +newtype Nat = Nat Word deriving newtype (Show, Eq, Ord, Enum, Bounded, Num, Real, Integral) --- | Unary natural numbers. Intended mainly for use at the type-level. -data Nat = Z | S Nat deriving (Show, Eq) +-- | Types supporting conversion to @Nat@. +-- +-- If @a@ also implements @FromNat@, then @toNat@ and @fromNat@ should be +-- inverses. +class ToNat a where + toNat :: a -> Nat -instance Ord Nat where - Z `compare` Z = EQ - S _ `compare` Z = GT - Z `compare` S _ = LT - S m `compare` S n = m `compare` n +-- | Types supporting conversion from @Nat@. +-- +-- If @a@ also implements @ToNat@, then @toNat@ and @fromNat@ should be +-- inverses. +class FromNat a where + fromNat :: Nat -> a -instance Num Nat where - Z + n = n - S m + n = S (m + n) +instance ToNat Nat where + toNat = id - n - Z = n - _ - S _ = throw Underflow +instance FromNat Nat where + fromNat = id - Z * _ = Z - S m * n = m * n + n +instance ToNat Word where + toNat = Nat - abs n = n - - signum Z = Z - signum (S _) = S Z - - fromInteger 0 = Z - fromInteger n - | n > 0 = S (fromInteger (n - 1)) - | otherwise = throw Underflow - -data CaseDecision f n where - IsZ :: CaseDecision f Z - IsS :: f n -> CaseDecision f (S n) - -type family Pred (n :: Nat) :: Nat where - Pred (S n) = n - -type family Add (x :: Nat) (y :: Nat) :: Nat where - Add Z n = n - Add (S m) n = S (Add m n) +instance FromNat Word where + fromNat (Nat n) = n diff --git a/src/Snats/Natural.hs b/src/Snats/Natural.hs @@ -0,0 +1,27 @@ +module Snats.Natural ( + Natural, + ToNatural (..), + FromNatural (..), +) where + +import Numeric.Natural + +-- | Types supporting conversion to @Natural@. +-- +-- If @a@ also implements @FromNatural@, then @toNatural@ and @fromNatural@ +-- should be inverses. +class ToNatural a where + toNatural :: a -> Natural + +-- | Types supporting conversion from @Natural@. +-- +-- If @a@ also implements @ToNatural@, then @toNatural@ and @fromNatural@ +-- should be inverses. +class FromNatural a where + fromNatural :: Natural -> a + +instance ToNatural Natural where + toNatural = id + +instance FromNatural Natural where + fromNatural = id diff --git a/src/Snats/Peano.hs b/src/Snats/Peano.hs @@ -0,0 +1,7 @@ +module Snats.Peano ( + module Snats.Peano.Alias, + module Snats.Peano.Prim, +) where + +import Snats.Peano.Alias +import Snats.Peano.Prim diff --git a/src/Snats/Peano/Alias.hs b/src/Snats/Peano/Alias.hs @@ -0,0 +1,47 @@ +module Snats.Peano.Alias where + +import Snats.Peano.Prim + +pattern SZ, SSZ, SSSZ, SSSSZ, SSSSSZ, SSSSSSZ, SSSSSSSZ, SSSSSSSSZ, SSSSSSSSSZ :: Peano +pattern SZ = S Z +pattern SSZ = S SZ +pattern SSSZ = S SSZ +pattern SSSSZ = S SSSZ +pattern SSSSSZ = S SSSSZ +pattern SSSSSSZ = S SSSSSZ +pattern SSSSSSSZ = S SSSSSSZ +pattern SSSSSSSSZ = S SSSSSSSZ +pattern SSSSSSSSSZ = S SSSSSSSSZ + +type SZ = S Z +type SSZ = S SZ +type SSSZ = S SSZ +type SSSSZ = S SSSZ +type SSSSSZ = S SSSSZ +type SSSSSSZ = S SSSSSZ +type SSSSSSSZ = S SSSSSSZ +type SSSSSSSSZ = S SSSSSSSZ +type SSSSSSSSSZ = S SSSSSSSSZ + +pattern Peano0, Peano1, Peano2, Peano3, Peano4, Peano5, Peano6, Peano7, Peano8, Peano9 :: Peano +pattern Peano0 = Z +pattern Peano1 = SZ +pattern Peano2 = SSZ +pattern Peano3 = SSSZ +pattern Peano4 = SSSSZ +pattern Peano5 = SSSSSZ +pattern Peano6 = SSSSSSZ +pattern Peano7 = SSSSSSSZ +pattern Peano8 = SSSSSSSSZ +pattern Peano9 = SSSSSSSSSZ + +type Peano0 = Z +type Peano1 = SZ +type Peano2 = SSZ +type Peano3 = SSSZ +type Peano4 = SSSSZ +type Peano5 = SSSSSZ +type Peano6 = SSSSSSZ +type Peano7 = SSSSSSSZ +type Peano8 = SSSSSSSSZ +type Peano9 = SSSSSSSSSZ diff --git a/src/Snats/Peano/Prim.hs b/src/Snats/Peano/Prim.hs @@ -0,0 +1,76 @@ +module Snats.Peano.Prim ( + Peano (..), + peanoShow, + peano, + peanoRec, + peanoFold, + Add, +) where + +import Control.Exception + +import Snats.Natural + +-- | Unary natural numbers. +-- Intended mainly for use at the type-level via DataKinds. +data Peano = Z | S Peano deriving (Eq) + +instance Show Peano where + show = show . toNatural + +-- | A variant of @show \@Peano@ that uses unary notation instead of decimal. +peanoShow :: Peano -> String +peanoShow Z = "Z" +peanoShow (S n) = 'S' : peanoShow n + +instance Ord Peano where + Z `compare` Z = EQ + S _ `compare` Z = GT + Z `compare` S _ = LT + S m `compare` S n = m `compare` n + +instance Num Peano where + Z + n = n + S m + n = S (m + n) + + n - Z = n + _ - S _ = throw Underflow + + Z * _ = Z + S m * n = m * n + n + + abs n = n + + signum Z = Z + signum (S _) = S Z + + fromInteger 0 = Z + fromInteger n + | n > 0 = S (fromInteger (n - 1)) + | otherwise = throw Underflow + +instance ToNatural Peano where + toNatural Z = 0 + toNatural (S n) = succ (toNatural n) + +instance FromNatural Peano where + fromNatural 0 = Z + fromNatural n = S (fromNatural (pred n)) + +-- | The (nonrecursive) eliminator for @Peano@ (equivalent to a case expression). +peano :: a -> (Peano -> a) -> Peano -> a +peano z _ Z = z +peano _ s (S m) = s m + +-- | The recursor for @Peano@. +peanoRec :: a -> (Peano -> a -> a) -> Peano -> a +peanoRec z _ Z = z +peanoRec z s (S m) = s m (peanoRec z s m) + +-- | The catamorphism for @Peano@. +peanoFold :: a -> (a -> a) -> Peano -> a +peanoFold z s = peanoRec z (const s) + +type family Add (x :: Peano) (y :: Peano) :: Peano where + Add Z n = n + Add (S m) n = S (Add m n) diff --git a/src/Snats/Snat.hs b/src/Snats/Snat.hs @@ -1,45 +1,9 @@ module Snats.Snat ( - module Snats.Nat, - Snat, -- Don't export the constructor! - pattern Zero, - pattern Succ, - toWord, - decideCase, - snat, - pred, - add, + module Snats.Peano, + module Snats.Snat.Alias, + module Snats.Snat.Prim, ) where -import Unsafe.Coerce -import Prelude hiding (pred, succ) - -import Snats.Nat - --- | Singleton natural numbers represented as a @Word@. -newtype Snat (n :: Nat) = SnatUnsafe Word deriving (Show, Eq) - -pattern Zero :: () => (n ~ Z) => Snat n -pattern Zero <- (decideCase -> IsZ) where Zero = SnatUnsafe 0 -pattern Succ :: () => (n ~ S m) => Snat m -> Snat n -pattern Succ p <- (decideCase -> IsS p) where Succ (SnatUnsafe n) = SnatUnsafe (n + 1) -{-# COMPLETE Zero, Succ #-} - -toWord :: Snat n -> Word -toWord (SnatUnsafe n) = n - -decideCase :: Snat n -> CaseDecision Snat n -decideCase (SnatUnsafe 0) = unsafeCoerce IsZ -decideCase (SnatUnsafe n) = unsafeCoerce IsS (SnatUnsafe (n - 1)) - -snat :: ((n ~ Z) => a) -> (forall m. (n ~ S m) => Snat m -> a) -> Snat n -> a -snat z _ Zero = z -snat _ s (Succ n) = s n - -pred :: Snat (S n) -> Snat n -pred (Succ n) = n - -add :: Snat m -> Snat n -> Snat (Add m n) -add (SnatUnsafe m) (SnatUnsafe n) = SnatUnsafe (m + n) - --- maybePred :: Snat n -> Maybe (Snat (Pred n)) --- maybePred = snat Nothing Just +import Snats.Peano +import Snats.Snat.Alias +import Snats.Snat.Prim diff --git a/src/Snats/Snat/Alias.hs b/src/Snats/Snat/Alias.hs @@ -0,0 +1,34 @@ +module Snats.Snat.Alias where + +import Snats.Peano +import Snats.Snat.Prim + +pattern Snat0 :: Snat Peano0 +pattern Snat0 = Zero + +pattern Snat1 :: Snat Peano1 +pattern Snat1 = Succ Snat0 + +pattern Snat2 :: Snat Peano2 +pattern Snat2 = Succ Snat1 + +pattern Snat3 :: Snat Peano3 +pattern Snat3 = Succ Snat2 + +pattern Snat4 :: Snat Peano4 +pattern Snat4 = Succ Snat3 + +pattern Snat5 :: Snat Peano5 +pattern Snat5 = Succ Snat4 + +pattern Snat6 :: Snat Peano6 +pattern Snat6 = Succ Snat5 + +pattern Snat7 :: Snat Peano7 +pattern Snat7 = Succ Snat6 + +pattern Snat8 :: Snat Peano8 +pattern Snat8 = Succ Snat7 + +pattern Snat9 :: Snat Peano9 +pattern Snat9 = Succ Snat8 diff --git a/src/Snats/Snat/Prim.hs b/src/Snats/Snat/Prim.hs @@ -0,0 +1,46 @@ +module Snats.Snat.Prim ( + Snat, + pattern Zero, + pattern Succ, + snat, + snatInd, + snatRec, + snatFold, + snatPred, + snatAdd, +) where + +import Data.Coerce +import Data.Functor.Const + +import Snats.Peano +import Snats.Snat.Unsafe (Snat, snatAdd, snatPeel, snatSucc, snatZero) +import Snats.Speano hiding (Succ, Zero) + +pattern Zero :: () => (n ~ Z) => Snat n +pattern Zero <- (snatPeel -> ZeroOp) where Zero = snatZero +pattern Succ :: () => (n ~ S m) => Snat m -> Snat n +pattern Succ p <- (snatPeel -> SuccOp p) where Succ = snatSucc +{-# COMPLETE Zero, Succ #-} + +-- | The (nonrecursive) eliminator for @Snat@ (equivalent to a case expression +-- with @Zero@ and @Succ@). +snat :: ((n ~ Z) => a) -> (forall m. (n ~ S m) => Snat m -> a) -> Snat n -> a +snat z _ Zero = z +snat _ s (Succ m) = s m + +-- | Induction for @Snat@. +snatInd :: f Z -> (forall m. Snat m -> f m -> f (S m)) -> Snat n -> f n +snatInd z _ Zero = z +snatInd z s (Succ m) = s m (snatInd z s m) + +-- | The (nondependent) recursor for @Snat@. +snatRec :: forall a n. a -> (forall m. Snat m -> a -> a) -> Snat n -> a +snatRec = coerce $ snatInd @(Const a) @n + +-- | The catamorphism for @Snat@. +snatFold :: a -> (a -> a) -> Snat n -> a +snatFold z s = snatRec z (const s) + +snatPred :: Snat (S n) -> Snat n +snatPred (Succ n) = n diff --git a/src/Snats/Snat/Unsafe.hs b/src/Snats/Snat/Unsafe.hs @@ -0,0 +1,35 @@ +module Snats.Snat.Unsafe where + +import GHC.Natural +import Unsafe.Coerce + +import Snats.Nat +import Snats.Natural +import Snats.Peano +import Snats.Speano + +-- | Singleton (@Peano@-indexed) natural numbers represented as a @Nat@. +newtype Snat (n :: Peano) = SnatUnsafe Nat deriving newtype (Show, Eq) + +instance ToNat (Snat n) where + toNat (SnatUnsafe n) = n + +instance ToNatural (Snat a) where + toNatural = wordToNatural . fromNat . toNat + +snatZero :: Snat Z +snatZero = SnatUnsafe 0 + +snatSucc :: Snat n -> Snat (S n) +snatSucc (SnatUnsafe n) = SnatUnsafe (succ n) + +-- | Peel the outermost unary constructor off a @Snat@, obtaining a result +-- suitable for shallow pattern matching. +-- +-- We only use @snatPeel@ to implement pattern synonyms. +snatPeel :: Snat n -> SpeanoSig Snat n +snatPeel (SnatUnsafe 0) = unsafeCoerce ZeroOp +snatPeel (SnatUnsafe n) = unsafeCoerce SuccOp (SnatUnsafe (pred n)) + +snatAdd :: Snat m -> Snat n -> Snat (Add m n) +snatAdd (SnatUnsafe m) (SnatUnsafe n) = SnatUnsafe (m + n) diff --git a/src/Snats/Snatural.hs b/src/Snats/Snatural.hs @@ -1,43 +1,9 @@ module Snats.Snatural ( - module Snats.Nat, - Snatural, -- Don't export the constructor! - pattern Zero, - pattern Succ, - toNatural, - decideCase, - snatural, - pred, - add, + module Snats.Peano, + module Snats.Snatural.Alias, + module Snats.Snatural.Prim, ) where -import Numeric.Natural -import Unsafe.Coerce -import Prelude hiding (pred, succ) - -import Snats.Nat - --- | Singleton natural numbers represented as a @Natural@. -newtype Snatural (n :: Nat) = SnaturalUnsafe Natural deriving (Show, Eq) - -pattern Zero :: () => (n ~ Z) => Snatural n -pattern Zero <- (decideCase -> IsZ) where Zero = SnaturalUnsafe 0 -pattern Succ :: () => (n ~ S m) => Snatural m -> Snatural n -pattern Succ p <- (decideCase -> IsS p) where Succ (SnaturalUnsafe n) = SnaturalUnsafe (n + 1) -{-# COMPLETE Zero, Succ #-} - -toNatural :: Snatural n -> Natural -toNatural (SnaturalUnsafe n) = n - -decideCase :: Snatural n -> CaseDecision Snatural n -decideCase (SnaturalUnsafe 0) = unsafeCoerce IsZ -decideCase (SnaturalUnsafe n) = unsafeCoerce IsS (SnaturalUnsafe (n - 1)) - -snatural :: ((n ~ Z) => a) -> (forall m. (n ~ S m) => Snatural m -> a) -> Snatural n -> a -snatural z _ Zero = z -snatural _ s (Succ n) = s n - -pred :: Snatural (S n) -> Snatural n -pred (Succ n) = n - -add :: Snatural m -> Snatural n -> Snatural (Add m n) -add (SnaturalUnsafe m) (SnaturalUnsafe n) = SnaturalUnsafe (m + n) +import Snats.Peano +import Snats.Snatural.Alias +import Snats.Snatural.Prim diff --git a/src/Snats/Snatural/Alias.hs b/src/Snats/Snatural/Alias.hs @@ -0,0 +1,34 @@ +module Snats.Snatural.Alias where + +import Snats.Peano +import Snats.Snatural.Prim + +pattern Snat0 :: Snatural Peano0 +pattern Snat0 = Zero + +pattern Snat1 :: Snatural Peano1 +pattern Snat1 = Succ Snat0 + +pattern Snat2 :: Snatural Peano2 +pattern Snat2 = Succ Snat1 + +pattern Snat3 :: Snatural Peano3 +pattern Snat3 = Succ Snat2 + +pattern Snat4 :: Snatural Peano4 +pattern Snat4 = Succ Snat3 + +pattern Snat5 :: Snatural Peano5 +pattern Snat5 = Succ Snat4 + +pattern Snat6 :: Snatural Peano6 +pattern Snat6 = Succ Snat5 + +pattern Snat7 :: Snatural Peano7 +pattern Snat7 = Succ Snat6 + +pattern Snat8 :: Snatural Peano8 +pattern Snat8 = Succ Snat7 + +pattern Snat9 :: Snatural Peano9 +pattern Snat9 = Succ Snat8 diff --git a/src/Snats/Snatural/Prim.hs b/src/Snats/Snatural/Prim.hs @@ -0,0 +1,46 @@ +module Snats.Snatural.Prim ( + Snatural, + pattern Zero, + pattern Succ, + snatural, + snaturalInd, + snaturalRec, + snaturalFold, + snaturalPred, + snaturalAdd, +) where + +import Data.Coerce +import Data.Functor.Const + +import Snats.Peano +import Snats.Snatural.Unsafe (Snatural, snaturalAdd, snaturalPeel, snaturalSucc, snaturalZero) +import Snats.Speano hiding (Succ, Zero) + +pattern Zero :: () => (n ~ Z) => Snatural n +pattern Zero <- (snaturalPeel -> ZeroOp) where Zero = snaturalZero +pattern Succ :: () => (n ~ S m) => Snatural m -> Snatural n +pattern Succ p <- (snaturalPeel -> SuccOp p) where Succ = snaturalSucc +{-# COMPLETE Zero, Succ #-} + +-- | The (nonrecursive) eliminator for @Snatural@ (equivalent to a case +-- expression with @Zero@ and @Succ@). +snatural :: ((n ~ Z) => a) -> (forall m. (n ~ S m) => Snatural m -> a) -> Snatural n -> a +snatural z _ Zero = z +snatural _ s (Succ m) = s m + +-- | Induction for @Snatural@. +snaturalInd :: f Z -> (forall m. Snatural m -> f m -> f (S m)) -> Snatural n -> f n +snaturalInd z _ Zero = z +snaturalInd z s (Succ m) = s m (snaturalInd z s m) + +-- | The (nondependent) recursor for @Snatural@. +snaturalRec :: forall a n. a -> (forall m. Snatural m -> a -> a) -> Snatural n -> a +snaturalRec = coerce $ snaturalInd @(Const a) @n + +-- | The catamorphism for @Snatural@. +snaturalFold :: a -> (a -> a) -> Snatural n -> a +snaturalFold z s = snaturalRec z (const s) + +snaturalPred :: Snatural (S n) -> Snatural n +snaturalPred (Succ n) = n diff --git a/src/Snats/Snatural/Unsafe.hs b/src/Snats/Snatural/Unsafe.hs @@ -0,0 +1,30 @@ +module Snats.Snatural.Unsafe where + +import Unsafe.Coerce + +import Snats.Natural +import Snats.Peano +import Snats.Speano + +-- | Singleton (@Peano@-indexed) natural numbers represented as a @Natural@. +newtype Snatural (n :: Peano) = SnaturalUnsafe Natural deriving (Show, Eq) + +instance ToNatural (Snatural n) where + toNatural (SnaturalUnsafe n) = n + +snaturalZero :: Snatural Z +snaturalZero = SnaturalUnsafe 0 + +snaturalSucc :: Snatural n -> Snatural (S n) +snaturalSucc (SnaturalUnsafe n) = SnaturalUnsafe (succ n) + +-- | Peel the outermost unary constructor off a @Snatural@, obtaining a result +-- suitable for shallow pattern matching. +-- +-- We only use @snaturalPeel@ to implement pattern synonyms. +snaturalPeel :: Snatural n -> SpeanoSig Snatural n +snaturalPeel (SnaturalUnsafe 0) = unsafeCoerce ZeroOp +snaturalPeel (SnaturalUnsafe n) = unsafeCoerce SuccOp (SnaturalUnsafe (pred n)) + +snaturalAdd :: Snatural m -> Snatural n -> Snatural (Add m n) +snaturalAdd (SnaturalUnsafe m) (SnaturalUnsafe n) = SnaturalUnsafe (m + n) diff --git a/src/Snats/Speano.hs b/src/Snats/Speano.hs @@ -0,0 +1,9 @@ +module Snats.Speano ( + module Snats.Peano, + module Snats.Speano.Alias, + module Snats.Speano.Prim, +) where + +import Snats.Peano +import Snats.Speano.Alias +import Snats.Speano.Prim diff --git a/src/Snats/Speano/Alias.hs b/src/Snats/Speano/Alias.hs @@ -0,0 +1,34 @@ +module Snats.Speano.Alias where + +import Snats.Peano +import Snats.Speano.Prim + +pattern Speano0 :: Speano Peano0 +pattern Speano0 = Zero + +pattern Speano1 :: Speano Peano1 +pattern Speano1 = Succ Speano0 + +pattern Speano2 :: Speano Peano2 +pattern Speano2 = Succ Speano1 + +pattern Speano3 :: Speano Peano3 +pattern Speano3 = Succ Speano2 + +pattern Speano4 :: Speano Peano4 +pattern Speano4 = Succ Speano3 + +pattern Speano5 :: Speano Peano5 +pattern Speano5 = Succ Speano4 + +pattern Speano6 :: Speano Peano6 +pattern Speano6 = Succ Speano5 + +pattern Speano7 :: Speano Peano7 +pattern Speano7 = Succ Speano6 + +pattern Speano8 :: Speano Peano8 +pattern Speano8 = Succ Speano7 + +pattern Speano9 :: Speano Peano9 +pattern Speano9 = Succ Speano8 diff --git a/src/Snats/Speano/Prim.hs b/src/Snats/Speano/Prim.hs @@ -0,0 +1,71 @@ +module Snats.Speano.Prim ( + SpeanoSig (..), + Speano (..), + speanoShow, + speano, + speanoInd, + speanoRec, + speanoFold, + speanoPred, + speanoAdd, +) where + +import Data.Coerce +import Data.Functor.Const + +import Snats.Natural +import Snats.Peano + +-- | The "signature" of singleton (@Peano@-indexed) natural numbers: +-- @SpeanoSig@ is to @Speano@ as @Maybe@ is to @Peano@. +data SpeanoSig f n where + ZeroOp :: SpeanoSig f Z + SuccOp :: f n -> SpeanoSig f (S n) + +-- | Singleton (@Peano@-indexed) unary natural numbers. +-- +-- @Speano@ is implemented as a GADT, and hence is inefficient. +-- See @Snat@ and @Snatural@ for efficient alternatives. +data Speano n where + Zero :: Speano Z + Succ :: Speano n -> Speano (S n) + +deriving instance Eq (Speano n) + +instance Show (Speano n) where + show = show . toNatural + +-- | A variant of @show \@(Speano n)@ that uses unary notation instead of decimal. +speanoShow :: Speano n -> String +speanoShow Zero = "Z" +speanoShow (Succ n) = 'S' : speanoShow n + +instance ToNatural (Speano n) where + toNatural Zero = 0 + toNatural (Succ n) = succ (toNatural n) + +-- | The (nonrecursive) eliminator for @Speano@ (equivalent to a case +-- expression). +speano :: ((n ~ Z) => a) -> (forall m. (n ~ S m) => Speano m -> a) -> Speano n -> a +speano z _ Zero = z +speano _ s (Succ m) = s m + +-- | Induction for @Speano@. +speanoInd :: f Z -> (forall m. Speano m -> f m -> f (S m)) -> Speano n -> f n +speanoInd z _ Zero = z +speanoInd z s (Succ m) = s m (speanoInd z s m) + +-- | The (nondependent) recursor for @Speano@. +speanoRec :: forall a n. a -> (forall m. Speano m -> a -> a) -> Speano n -> a +speanoRec = coerce $ speanoInd @(Const a) @n + +-- | The catamorphism for @Speano@. +speanoFold :: a -> (a -> a) -> Speano n -> a +speanoFold z s = speanoRec z (const s) + +speanoPred :: Speano (S n) -> Speano n +speanoPred (Succ n) = n + +speanoAdd :: Speano m -> Speano n -> Speano (Add m n) +speanoAdd Zero n = n +speanoAdd (Succ m) n = Succ (speanoAdd m n)