packages feed

type-level-show-0.2.0: src/TypeLevelShow/Natural/Digit.hs

{-# LANGUAGE UndecidableInstances #-} -- for ErrorPDoc

{- | Unsafe 'Natural' digit renderers.

These are used by "TypeLevelShow.Natural", where modulo arithmetic guarantees
that they are not used unsafely.
-}

module TypeLevelShow.Natural.Digit
  ( ShowNatDigitHexLower, ShowNatDigitHexLowerSym, sShowNatDigitHexLower
  , ShowNatDigitHexUpper, ShowNatDigitHexUpperSym, sShowNatDigitHexUpper
  , ENotDigitOfBase
  ) where

import GHC.TypeNats
import DeFun.Core
import Singleraeh.Equality ( testEqElse )
import TypeLevelShow.Doc
import GHC.TypeLits hiding ( ErrorMessage(..) )

-- | Type-level error message displayed when a digit renderer receives a
--   digit not present in the base it handles.
--
-- Note that we can't show the digit, unless we use 'ShowType'. Which
-- type-level-show is about avoiding. Not really an issue, it should be clear in
-- the type error.
type ENotDigitOfBase base = Text "got non-" :<>: Text base :<>: Text " digit"

-- | Return the associated lower-case 'Char' for a 'Natural' hexadecimal digit.
--
-- Unsafe. Must be called with 0-15.
type family ShowNatDigitHexLower (d :: Natural) :: Char where
    ShowNatDigitHexLower  0 = '0'
    ShowNatDigitHexLower  1 = '1'
    ShowNatDigitHexLower  2 = '2'
    ShowNatDigitHexLower  3 = '3'
    ShowNatDigitHexLower  4 = '4'
    ShowNatDigitHexLower  5 = '5'
    ShowNatDigitHexLower  6 = '6'
    ShowNatDigitHexLower  7 = '7'
    ShowNatDigitHexLower  8 = '8'
    ShowNatDigitHexLower  9 = '9'
    ShowNatDigitHexLower 10 = 'a'
    ShowNatDigitHexLower 11 = 'b'
    ShowNatDigitHexLower 12 = 'c'
    ShowNatDigitHexLower 13 = 'd'
    ShowNatDigitHexLower 14 = 'e'
    ShowNatDigitHexLower 15 = 'f'
    ShowNatDigitHexLower  n = ErrorPDoc (ENotDigitOfBase "hexadecimal")

type ShowNatDigitHexLowerSym :: Natural ~> Char
data ShowNatDigitHexLowerSym d
type instance App ShowNatDigitHexLowerSym d = ShowNatDigitHexLower d

sShowNatDigitHexLower :: Lam SNat SChar ShowNatDigitHexLowerSym
sShowNatDigitHexLower = Lam $ \sn ->
      testEqElse sn (SNat  @0) (SChar @'0')
    $ testEqElse sn (SNat  @1) (SChar @'1')
    $ testEqElse sn (SNat  @2) (SChar @'2')
    $ testEqElse sn (SNat  @3) (SChar @'3')
    $ testEqElse sn (SNat  @4) (SChar @'4')
    $ testEqElse sn (SNat  @5) (SChar @'5')
    $ testEqElse sn (SNat  @6) (SChar @'6')
    $ testEqElse sn (SNat  @7) (SChar @'7')
    $ testEqElse sn (SNat  @8) (SChar @'8')
    $ testEqElse sn (SNat  @9) (SChar @'9')
    $ testEqElse sn (SNat @10) (SChar @'a')
    $ testEqElse sn (SNat @11) (SChar @'b')
    $ testEqElse sn (SNat @12) (SChar @'c')
    $ testEqElse sn (SNat @13) (SChar @'d')
    $ testEqElse sn (SNat @14) (SChar @'e')
    $ testEqElse sn (SNat @15) (SChar @'f')
    $ errorPDoc @(ENotDigitOfBase "hexadecimal")

-- | Return the associated upper-case 'Char' for a 'Natural' hexadecimal digit.
--
-- Unsafe. Must be called with 0-15.
type family ShowNatDigitHexUpper (d :: Natural) :: Char where
    ShowNatDigitHexUpper  0 = '0'
    ShowNatDigitHexUpper  1 = '1'
    ShowNatDigitHexUpper  2 = '2'
    ShowNatDigitHexUpper  3 = '3'
    ShowNatDigitHexUpper  4 = '4'
    ShowNatDigitHexUpper  5 = '5'
    ShowNatDigitHexUpper  6 = '6'
    ShowNatDigitHexUpper  7 = '7'
    ShowNatDigitHexUpper  8 = '8'
    ShowNatDigitHexUpper  9 = '9'
    ShowNatDigitHexUpper 10 = 'A'
    ShowNatDigitHexUpper 11 = 'B'
    ShowNatDigitHexUpper 12 = 'C'
    ShowNatDigitHexUpper 13 = 'D'
    ShowNatDigitHexUpper 14 = 'E'
    ShowNatDigitHexUpper 15 = 'F'

type ShowNatDigitHexUpperSym :: Natural ~> Char
data ShowNatDigitHexUpperSym d
type instance App ShowNatDigitHexUpperSym d = ShowNatDigitHexUpper d

sShowNatDigitHexUpper :: Lam SNat SChar ShowNatDigitHexUpperSym
sShowNatDigitHexUpper = Lam $ \sn ->
      testEqElse sn (SNat  @0) (SChar @'0')
    $ testEqElse sn (SNat  @1) (SChar @'1')
    $ testEqElse sn (SNat  @2) (SChar @'2')
    $ testEqElse sn (SNat  @3) (SChar @'3')
    $ testEqElse sn (SNat  @4) (SChar @'4')
    $ testEqElse sn (SNat  @5) (SChar @'5')
    $ testEqElse sn (SNat  @6) (SChar @'6')
    $ testEqElse sn (SNat  @7) (SChar @'7')
    $ testEqElse sn (SNat  @8) (SChar @'8')
    $ testEqElse sn (SNat  @9) (SChar @'9')
    $ testEqElse sn (SNat @10) (SChar @'A')
    $ testEqElse sn (SNat @11) (SChar @'B')
    $ testEqElse sn (SNat @12) (SChar @'C')
    $ testEqElse sn (SNat @13) (SChar @'D')
    $ testEqElse sn (SNat @14) (SChar @'E')
    $ testEqElse sn (SNat @15) (SChar @'F')
    $ errorPDoc @(ENotDigitOfBase "hexadecimal")