singletons-base-3.2: src/GHC/TypeLits/Singletons.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-----------------------------------------------------------------------------
-- |
-- Module : GHC.TypeLits.Singletons
-- Copyright : (C) 2014 Richard Eisenberg
-- License : BSD-style (see LICENSE)
-- Maintainer : Ryan Scott
-- Stability : experimental
-- Portability : non-portable
--
-- Defines and exports singletons useful for the Natural, Symbol, and Char
-- kinds.
--
----------------------------------------------------------------------------
module GHC.TypeLits.Singletons (
Natural, Symbol, Char,
Sing,
SNat, pattern SNat,
SSymbol, pattern SSymbol, pattern SSym,
SChar, pattern SChar,
withKnownNat, withKnownSymbol, withKnownChar,
Error, sError,
ErrorWithoutStackTrace, sErrorWithoutStackTrace,
Undefined, sUndefined,
KnownNat, natVal,
KnownSymbol, symbolVal,
KnownChar, charVal,
type (^), (%^),
type (<=?), (%<=?),
TN.Log2, sLog2,
Div, sDiv, Mod, sMod, DivMod, sDivMod,
Quot, sQuot, Rem, sRem, QuotRem, sQuotRem,
consSymbol, ConsSymbol, sConsSymbol,
unconsSymbol, UnconsSymbol, sUnconsSymbol,
charToNat, CharToNat, sCharToNat,
natToChar, NatToChar, sNatToChar,
-- * Defunctionalization symbols
ErrorSym0, ErrorSym1,
ErrorWithoutStackTraceSym0, ErrorWithoutStackTraceSym1,
UndefinedSym0,
KnownNatSym0, KnownNatSym1,
KnownSymbolSym0, KnownSymbolSym1,
KnownCharSym0, KnownCharSym1,
type (^@#@$), type (^@#@$$), type (^@#@$$$),
type (<=?@#@$), type (<=?@#@$$), type (<=?@#@$$$),
Log2Sym0, Log2Sym1,
DivSym0, DivSym1, DivSym2,
ModSym0, ModSym1, ModSym2,
DivModSym0, DivModSym1, DivModSym2,
QuotSym0, QuotSym1, QuotSym2,
RemSym0, RemSym1, RemSym2,
QuotRemSym0, QuotRemSym1, QuotRemSym2,
ConsSymbolSym0, ConsSymbolSym1, ConsSymbolSym2,
UnconsSymbolSym0, UnconsSymbolSym1,
CharToNatSym0, CharToNatSym1,
NatToCharSym0, NatToCharSym1
) where
import Data.Char (chr, ord)
import qualified Data.List as L (uncons)
import Data.Singletons
import Data.Singletons.TH
import Data.String (IsString(..))
import qualified Data.Text as T
import Data.Tuple.Singletons
import GHC.TypeLits ( CharToNat, ConsSymbol, NatToChar, UnconsSymbol
, withSomeSChar, withSomeSSymbol )
import GHC.TypeLits.Singletons.Internal
import qualified GHC.TypeNats as TN
import GHC.TypeNats (Div, Mod)
import Unsafe.Coerce
-- | This bogus instance is helpful for people who want to define
-- functions over Symbols that will only be used at the type level or
-- as singletons.
instance Eq Symbol where
(==) = no_term_level_syms
instance Ord Symbol where
compare = no_term_level_syms
instance IsString Symbol where
fromString = no_term_level_syms
instance Semigroup Symbol where
(<>) = no_term_level_syms
instance Monoid Symbol where
mempty = no_term_level_syms
instance Show Symbol where
showsPrec = no_term_level_syms
no_term_level_syms :: a
no_term_level_syms = error "The kind `Symbol` may not be used at the term level."
-- These are often useful in TypeLits-heavy code
$(genDefunSymbols [''KnownNat, ''KnownSymbol, ''KnownChar])
------------------------------------------------------------
-- Log2, Div, Mod, DivMod, and friends
------------------------------------------------------------
{- | Adapted from GHC's source code.
Compute the logarithm of a number in the given base, rounded down to the
closest integer. -}
genLog2 :: Natural -> Natural
genLog2 x = exactLoop 0 x
where
exactLoop s i
| i == 1 = s
| i < 2 = s
| otherwise =
let s1 = s + 1
in s1 `seq` case divMod i 2 of
(j,r)
| r == 0 -> exactLoop s1 j
| otherwise -> underLoop s1 j
underLoop s i
| i < 2 = s
| otherwise = let s1 = s + 1 in s1 `seq` underLoop s1 (div i 2)
sLog2 :: Sing x -> Sing (TN.Log2 x)
sLog2 sx =
let x = fromSing sx
in case x of
0 -> error "log2 of 0"
_ -> TN.withSomeSNat (genLog2 x) unsafeCoerce
$(genDefunSymbols [''TN.Log2])
instance SingI Log2Sym0 where
sing = singFun1 sLog2
sDiv :: Sing x -> Sing y -> Sing (Div x y)
sDiv sx sy =
let x = fromSing sx
y = fromSing sy
in TN.withSomeSNat (x `div` y) unsafeCoerce
infixl 7 `sDiv`
$(genDefunSymbols [''Div])
instance SingI DivSym0 where
sing = singFun2 sDiv
instance SingI x => SingI (DivSym1 x) where
sing = singFun1 $ sDiv (sing @x)
instance SingI1 DivSym1 where
liftSing s = singFun1 $ sDiv s
sMod :: Sing x -> Sing y -> Sing (Mod x y)
sMod sx sy =
let x = fromSing sx
y = fromSing sy
in TN.withSomeSNat (x `mod` y) unsafeCoerce
infixl 7 `sMod`
$(genDefunSymbols [''Mod])
instance SingI ModSym0 where
sing = singFun2 sMod
instance SingI x => SingI (ModSym1 x) where
sing = singFun1 $ sMod $ sing @x
instance SingI1 ModSym1 where
liftSing s = singFun1 $ sMod s
$(promoteOnly [d|
divMod :: Natural -> Natural -> (Natural, Natural)
divMod x y = (div x y, mod x y)
quotRem :: Natural -> Natural -> (Natural, Natural)
quotRem = divMod
quot :: Natural -> Natural -> Natural
quot = div
infixl 7 `quot`
rem :: Natural -> Natural -> Natural
rem = mod
infixl 7 `rem`
|])
sDivMod :: Sing x -> Sing y -> Sing (DivMod x y)
sDivMod sx sy =
let x = fromSing sx
y = fromSing sy
(q,r) = x `divMod` y
in TN.withSomeSNat q $ \sq ->
TN.withSomeSNat r $ \sr ->
unsafeCoerce (STuple2 sq sr)
sQuotRem :: Sing x -> Sing y -> Sing (QuotRem x y)
sQuotRem = sDivMod
sQuot :: Sing x -> Sing y -> Sing (Quot x y)
sQuot = sDiv
infixl 7 `sQuot`
sRem :: Sing x -> Sing y -> Sing (Rem x y)
sRem = sMod
infixl 7 `sRem`
consSymbol :: Char -> String -> String
consSymbol = (:)
sConsSymbol :: Sing x -> Sing y -> Sing (ConsSymbol x y)
sConsSymbol sx sy =
let x = fromSing sx
y = T.unpack (fromSing sy)
in withSomeSSymbol (consSymbol x y) unsafeCoerce
$(genDefunSymbols [''ConsSymbol])
instance SingI ConsSymbolSym0 where
sing = singFun2 sConsSymbol
instance SingI x => SingI (ConsSymbolSym1 x) where
sing = singFun1 $ sConsSymbol $ sing @x
instance SingI1 ConsSymbolSym1 where
liftSing s = singFun1 $ sConsSymbol s
unconsSymbol :: String -> Maybe (Char, String)
unconsSymbol = L.uncons
sUnconsSymbol :: Sing x -> Sing (UnconsSymbol x)
sUnconsSymbol sx =
let x = T.unpack (fromSing sx)
res = toSing (unconsSymbol x)
in case res of
SomeSing s -> unsafeCoerce s
$(genDefunSymbols [''UnconsSymbol])
instance SingI UnconsSymbolSym0 where
sing = singFun1 sUnconsSymbol
charToNat :: Char -> Natural
charToNat = fromIntegral . ord
sCharToNat :: Sing x -> Sing (CharToNat x)
sCharToNat sx =
let x = fromSing sx
in TN.withSomeSNat (charToNat x) unsafeCoerce
$(genDefunSymbols [''CharToNat])
instance SingI CharToNatSym0 where
sing = singFun1 sCharToNat
natToChar :: Natural -> Char
natToChar = chr . fromIntegral
sNatToChar :: Sing x -> Sing (NatToChar x)
sNatToChar sx =
let x = fromSing sx
in withSomeSChar (natToChar x) unsafeCoerce
$(genDefunSymbols [''NatToChar])
instance SingI NatToCharSym0 where
sing = singFun1 sNatToChar