Copyright | (c) Justin Le 2016 |
---|---|
License | MIT |
Maintainer | justin@jle.im |
Stability | unstable |
Portability | non-portable |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
This module essentially provides a lightweight subset of the
singletons library specifically for Nat
and Symbol
, from
GHC.TypeLits.
Its main functionality is for first-class manipulation of KnownNat
and
KnownSymbol
constraints. For example, in general, if you have
, GHC can't infer KnownNat
n
. And, if you have
both KnownNat
(n + 1)
and KnownNat
n
, GHC can't infer KnownNat
m'KnownNat (n
+ m)
.
This can be annoying when dealing with libraries and applications where
one regularly adds and subtracts type-level nats and expects KnownNat
instances to follow. For example, vector concatenation of
length-encoded vector types can be:
concat :: (KnownNat
n,KnownNat
m) => Vector n a -> Vector m a -> Vector (n + m) a
But, now n + m
does not have a KnownNat
instance...which makes
operations like this much less useful.
Usually, the easiest way to get around this is to use a typechecker plugin, like https://hackage.haskell.org/package/ghc-typelits-knownnat. However, we can do this without the help of a typechecker plugin using first-class values, at the cost of some increased verbosity.
We introduce
, which is a term-level witness of knownnat-ness
that can be manipulated as a first-class value.SNat
n
If we have
, we can construct an KnownNat
n
:SNat
n
SNat
:: KnownNat n -> SNat n
Furthermore, if we have an
, we can pattern match on the
SNat
nSNat
constructor to get a
constraint:KnownNat
n
myFunc :: SNat n -> Bool myFunc SNat = ... -- in this body, we have `KnownNat n`
So if we have
and KnownNat
n
, we can get KnownNat
m
by using KnownNat
(n + m)%+
, which adds together SNat
s:
case (SNat :: SNat n) %+ (SNat :: SNat m) of SNat -> -- in this branch, we have `KnownNat (n + m)`
Note that this module converts between SNat
and Natural
, and not
SNat
and Integer
, in GHC.TypeNats-style.
Of course, all of this functionality is provided by the singletons
library, in Data.Singletons.TypeLits. This module can be useful if
you want a lightweight alternative without the full might of
singletons. The main benefit of the singletons library is providing
a unified interface for singletons of all different kinds/types, and
not just Natural
and String
.
Synopsis
- data SNat n = KnownNat n => SNat
- data SomeNat where
- data Natural where
- fromSNat :: SNat n -> Natural
- withKnownNat :: SNat n -> (KnownNat n => r) -> r
- withSomeNat :: Natural -> (forall n. SNat n -> r) -> r
- toSomeNat :: Natural -> SomeNat
- (%+) :: SNat n -> SNat m -> SNat (n + m)
- (%-) :: SNat n -> SNat m -> SNat (n - m)
- minusSNat :: SNat n -> SNat m -> Either (CmpNat n m :~: 'LT) (SNat (n - m))
- minusSNat_ :: SNat n -> SNat m -> Maybe (SNat (n - m))
- (%*) :: SNat n -> SNat m -> SNat (n * m)
- (%^) :: SNat n -> SNat m -> SNat (n ^ m)
- (%<=?) :: SNat n -> SNat m -> n :<=? m
- sCmpNat :: SNat n -> SNat m -> SCmpNat n m
- unsafeLiftNatOp1 :: (Natural -> Natural) -> SNat n -> SNat m
- unsafeLiftNatOp2 :: (Natural -> Natural -> Natural) -> SNat n -> SNat m -> SNat o
- data SSymbol n = KnownSymbol n => SSymbol
- data SomeSymbol where
- pattern SomeSymbol_ :: SSymbol n -> SomeSymbol
- pattern FromSSymbol :: SSymbol n -> String
- fromSSymbol :: SSymbol n -> String
- withKnownSymbol :: SSymbol n -> (KnownSymbol n => r) -> r
- withSomeSymbol :: String -> (forall n. SSymbol n -> r) -> r
- toSomeSymbol :: String -> SomeSymbol
Nats
An
is a witness for SNat
n
.KnownNat
n
This means that if you pattern match on the SNat
constructor, in that
branch you will have a
constraint.KnownNat
n
myFunc :: SNat n -> Bool myFunc SNat = ... -- in this body, we have `KnownNat n`
This is essentially a singleton for Nat
, and stands in for the
singletons SNat
and Sing
types.
This type represents unknown type-level natural numbers.
Since: base-4.10.0.0
pattern SomeNat_ :: SNat n -> SomeNat | A useful pattern synonym for matching on a A layer of compatibility letting us use the original This stands in for the singletons |
Natural number
Invariant: numbers <= 0xffffffffffffffff use the NS
constructor
pattern FromSNat :: SNat n -> Natural | A useful pattern synonym for matching on a myFunc :: Natural -> Bool
myFunc (FromSNat x) = ... -- x is `SNat n`, with It can be used as a function, as well, to convert an This stands in for the singletons |
Instances
Enum Natural | Since: base-4.8.0.0 |
Num Natural | Note that Since: base-4.8.0.0 |
Read Natural | Since: base-4.8.0.0 |
Integral Natural | Since: base-4.8.0.0 |
Defined in GHC.Real | |
Real Natural | Since: base-4.8.0.0 |
Defined in GHC.Real toRational :: Natural -> Rational # | |
Show Natural | Since: base-4.8.0.0 |
Eq Natural | |
Ord Natural | |
TestEquality SNat Source # | |
Defined in GHC.TypeLits.Witnesses | |
GCompare SNat Source # | |
GEq SNat Source # | |
GShow SNat Source # | |
Defined in GHC.TypeLits.Witnesses gshowsPrec :: forall (a :: k). Int -> SNat a -> ShowS # | |
type Compare (a :: Natural) (b :: Natural) | |
Defined in Data.Type.Ord |
withKnownNat :: SNat n -> (KnownNat n => r) -> r Source #
Given an
and a value that would require a SNat
n
instance, create that value.KnownNat
n
This stands in for the function of the same name from Data.Singletons.TypeLits.
withSomeNat :: Natural -> (forall n. SNat n -> r) -> r Source #
Promote ("reify") a Natural
to an
, by providing
a continuation that would handle it in a way that is polymorphic over
all possible SNat
nn
.
This stands in the singletons withSomeSing
function.
toSomeNat :: Natural -> SomeNat Source #
Promote ("reify") a Natural
to an
existentially hidden
inside a SNat
nSomeNat
. To use it, pattern match using SomeNat_
.
This stands in the singletons toSomeSing
function.
Operations
(%+) :: SNat n -> SNat m -> SNat (n + m) Source #
Addition of SNat
s.
This also will provide the correct KnownNat
instance for
, so can be used as a way to "add" SNat
(n
+ m)KnownNat
instances.
This stands in for the function with the same name from Data.Singletons.Prelude.Num.
(%-) :: SNat n -> SNat m -> SNat (n - m) Source #
Subtraction of SNat
s. Note that this is unsafe, as will trigger
a run-time underflow if m
is bigger than n
even though it will always
succeed at compiletime.
This also will provide the correct KnownNat
instance for
, so can be used as a way to "subtract" SNat
(n
- m)KnownNat
instances.
This stands in for the function with the same name from Data.Singletons.Prelude.Num.
(%*) :: SNat n -> SNat m -> SNat (n * m) Source #
Multiplication of SNat
s.
This also will provide the correct KnownNat
instance for
, so can be used as a way to "multiply" SNat
(n
* m)KnownNat
instances.
This stands in for the function with the same name from Data.Singletons.Prelude.Num.
(%^) :: SNat n -> SNat m -> SNat (n ^ m) Source #
Exponentiation of SNat
s.
This also will provide the correct KnownNat
instance for
, so can be used as a way to "exponentiate" SNat
(n
^ m)KnownNat
instances.
This stands in for the function with the same name from Data.Singletons.TypeLits.
Compare
(%<=?) :: SNat n -> SNat m -> n :<=? m Source #
Compare n
and m
, categorizing them into one of the constructors of
:<=?
.
sCmpNat :: SNat n -> SNat m -> SCmpNat n m Source #
Compare n
and m
, categorizing them into one of the constructors of
SCmpNat
.
Unsafe
unsafeLiftNatOp1 :: (Natural -> Natural) -> SNat n -> SNat m Source #
Lift a unary operation to act on an
that returns an SNat
n
. The function given must properly describe the relationship between
SNat
mn
and m
.
For example:
double :: SNat n -> SNat (n * 2) double = unsafeLiftNatOp1 (*2)
The correctness of the relationship is not checked, so be aware that this can cause programs to break.
unsafeLiftNatOp2 :: (Natural -> Natural -> Natural) -> SNat n -> SNat m -> SNat o Source #
Lift a binary operation to act on an
and SNat
n
that
returns an SNat
m
. The function given must properly describe the
relationship between SNat
on
, m
, and o
.
For example:
multiply :: SNat n -> SNat m -> SNat (n * m) multiply = unsafeLiftNatOp2 (*)
The correctness of the relationship is not checked, so be aware that this can cause programs to break.
Symbols
An
is a witness for SSymbol
n
.KnownSymbol
n
This means that if you pattern match on the SSymbol
constructor, in that
branch you will have a
constraint.KnownSymbol
n
myFunc :: SSymbol n -> Bool myFunc SSymbol = ... -- in this body, we have `KnownSymbol n`
This is essentially a singleton for Symbol
, and stands in for the
singletons SSymbol
and Sing
types.
KnownSymbol n => SSymbol |
Instances
TestEquality SSymbol Source # | |
Defined in GHC.TypeLits.Witnesses | |
GCompare SSymbol Source # | |
GEq SSymbol Source # | |
GShow SSymbol Source # | |
Defined in GHC.TypeLits.Witnesses gshowsPrec :: forall (a :: k). Int -> SSymbol a -> ShowS # | |
Show (SSymbol n) Source # | |
Eq (SSymbol n) Source # | |
Ord (SSymbol n) Source # | |
Defined in GHC.TypeLits.Witnesses |
data SomeSymbol where #
This type represents unknown type-level symbols.
pattern SomeSymbol_ :: SSymbol n -> SomeSymbol | A useful pattern synonym for matching on a A layer of compatibility letting us use the original This stands in for the singletons |
Instances
Read SomeSymbol | Since: base-4.7.0.0 |
Defined in GHC.TypeLits readsPrec :: Int -> ReadS SomeSymbol # readList :: ReadS [SomeSymbol] # readPrec :: ReadPrec SomeSymbol # readListPrec :: ReadPrec [SomeSymbol] # | |
Show SomeSymbol | Since: base-4.7.0.0 |
Defined in GHC.TypeLits showsPrec :: Int -> SomeSymbol -> ShowS # show :: SomeSymbol -> String # showList :: [SomeSymbol] -> ShowS # | |
Eq SomeSymbol | Since: base-4.7.0.0 |
Defined in GHC.TypeLits (==) :: SomeSymbol -> SomeSymbol -> Bool # (/=) :: SomeSymbol -> SomeSymbol -> Bool # | |
Ord SomeSymbol | Since: base-4.7.0.0 |
Defined in GHC.TypeLits compare :: SomeSymbol -> SomeSymbol -> Ordering # (<) :: SomeSymbol -> SomeSymbol -> Bool # (<=) :: SomeSymbol -> SomeSymbol -> Bool # (>) :: SomeSymbol -> SomeSymbol -> Bool # (>=) :: SomeSymbol -> SomeSymbol -> Bool # max :: SomeSymbol -> SomeSymbol -> SomeSymbol # min :: SomeSymbol -> SomeSymbol -> SomeSymbol # |
pattern FromSSymbol :: SSymbol n -> String Source #
A useful pattern synonym for matching on a String
as if it "were"
a SSymbol
:
myFunc :: String -> Bool
myFunc (FromSSymbol x) = ... -- x is `SSymbol n`, with n
coming from the input
It can be used as a function, as well, to convert an
back
into the SSymbol
nString
that it represents.
This stands in for the singletons FromSing
pattern synonym, except
it matches on a String
instead of a Text
.
fromSSymbol :: SSymbol n -> String Source #
withKnownSymbol :: SSymbol n -> (KnownSymbol n => r) -> r Source #
Given an
and a value that would require a SSymbol
n
instance, create that value.KnownSymbol
n
This stands in for the function of the same name from Data.Singletons.TypeLits.
withSomeSymbol :: String -> (forall n. SSymbol n -> r) -> r Source #
Promote ("reify") a String
to an
, by providing
a continuation that would handle it in a way that is polymorphic over
all possible SSymbol
nn
.
This stands in the singletons withSomeSing
function, except it takes
a String
instead of Text
.
toSomeSymbol :: String -> SomeSymbol Source #
Promote ("reify") a String
to an
existentially hidden
inside a SSymbol
nSomeNat
. To use it, pattern match using SomeSymbol_
.
This stands in the singletons toSomeSing
function, except it takes
a String
instead of Text
.