| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Data.SNumber
Description
Runtime witnesses of type-level integers.
Synopsis
- data SNumber a (n :: Integer) where
- class Integral a => SNumberRepr a where
- type SafeSNumber a :: Integer -> Constraint
- snumber :: forall n a. (SNumberRepr a, SafeSNumber a n, KnownInteger n) => SNumber a n
- trySNumber :: forall n a. (SNumberRepr a, KnownInteger n) => Maybe (SNumber a n)
- unsafeUncheckedSNumber :: forall n a. (Num a, KnownInteger n) => SNumber a n
- unsafeMkSNumber :: forall n a. SafeSNumber a n => a -> SNumber a n
- unsafeTryMkSNumber :: forall n a. SNumberRepr a => Integer -> Maybe (SNumber a n)
- unsafeUncheckedMkSNumber :: forall n a. a -> SNumber a n
- data SomeSNumber a = forall n. SomeSNumber (SNumber a n)
- someSNumberVal :: SNumberRepr a => a -> SomeSNumber a
- withSNumber :: SNumberRepr a => a -> (forall n. SNumber a n -> r) -> r
- compareSNumber :: forall m n a. Ord a => SNumber a m -> SNumber a n -> OrderingI m n
- sameSNumber :: Eq a => SNumber a m -> SNumber a n -> Maybe (m :~: n)
- class Integral a => OverflowArith a where
- overflowAdd :: a -> a -> (Bool, a)
- overflowSub :: a -> a -> (Bool, a)
- overflowMul :: a -> a -> (Bool, a)
- tryAdd :: (SNumberRepr a, OverflowArith a) => SNumber a m -> SNumber a n -> Maybe (SNumber a (m + n))
- trySub :: (SNumberRepr a, OverflowArith a) => SNumber a m -> SNumber a n -> Maybe (SNumber a (m - n))
- tryMul :: (SNumberRepr a, OverflowArith a) => SNumber a m -> SNumber a n -> Maybe (SNumber a (m * n))
- data UnrepresentableSNumber = UnrepresentableSNumber String Integer Integer
- chkAdd :: (SNumberRepr a, OverflowArith a, HasCallStack) => SNumber a m -> SNumber a n -> SNumber a (m + n)
- chkSub :: (SNumberRepr a, OverflowArith a, HasCallStack) => SNumber a m -> SNumber a n -> SNumber a (m - n)
- chkMul :: (SNumberRepr a, OverflowArith a, HasCallStack) => SNumber a m -> SNumber a n -> SNumber a (m * n)
- divExact :: SNumberRepr a => SNumber a (m * n) -> SNumber a n -> SNumber a m
- class KnownSNumber a n where
- snumberVal_ :: SNumber a n
- snumberVal :: forall n a. KnownSNumber a n => SNumber a n
- reifySNumber :: forall a n r. SNumber a n -> (KnownSNumber a n => r) -> r
- reifySNumberAsNat :: forall n r a. Integral a => SNumber a ('Pos n) -> (KnownNat n => r) -> r
- type IntBits = 64
- type IntMin = 'Neg (2 ^ (IntBits - 1))
- type IntMaxP1 = 'Pos (2 ^ (IntBits - 1))
- type WordBits = 64
- type WordMaxP1 = 'Pos (2 ^ WordBits)
SNumber
data SNumber a (n :: Integer) where Source #
SNumber a n is a runtime representation of the value of n.
For N# n :: SNumber a n and N# m :: SNumber a m, the following must hold:
n ~ miffn == m(according to theEqinstance ofa).CmpInteger m n ~ 'LTiffcompare m n == LT(according toOrd).CmpInteger m n ~ 'EQiffcompare m n == EQ.CmpInteger m n ~ 'GTiffcompare m n == GT.toInteger n == integerVal @n.aupholds theOrdlaws.
These are exactly the set of things we're willing to unsafeCoerce proofs
for. It is unsafe to construct an SNumber that violates these by any
means.
Note that the first requirement means that you must never construct
N# 0 :: SNumber _ ('Neg 0), because that would prove that
'Neg 0 ~ 'Pos 0. In practice, we largely ignore the existence of
'Neg 0: trySNumber (and, by extension, the instance derivation via
KnownInteger) will throw a runtime error when trying to construct
'Neg 0, and SafeSNumber instances explicitly exclude 'Neg 0 with
type-level checks.
There are six main ways to introduce an SNumber, from the cartesian
product of several choices
How do you validate that the value is in-bounds for the representation type?
- If by runtime checks, the function name has a "try" infix.
- If by trusting the caller blindly, the function name has an "unsafe" prefix and an "Unchecked" infix.
- If by type-level bounds checks, the function name has neither of these.
How do you validate that the runtime Integer value matches the type-level Integer?
- If by trusting the user to pass in the right Integer, the function has
an "unsafe" prefix and a "Mk" infix: we're "making" the
SNumberfrom a runtime value rather than deriving it from KnownNat. - If by getting it from a KnownNat instance, the function name doesn't have that infix.
- If by trusting the user to pass in the right Integer, the function has
an "unsafe" prefix and a "Mk" infix: we're "making" the
Thus:
snumber: type-level checks, safeKnownNat.trySNumber: runtime checks, safeKnownNat.unsafeUncheckedSNumber: no checks, safeKnownNat.unsafeMkSNumber: type-level checks, unsafeIntegerparameter.unsafeTryMkSNumber: runtime checks, unsafeIntegerparameter.unsafeUncheckedMkSNumber: no checks, unsafeaparameter. a.k.a.N#.
Finally, there's one other way to get an SNumber: by asking the constraint
solver to give you one, with a KnownSNumber class and its snumberVal
method. Currently, this will be solved automatically from KnownNat by
using trySNumber, and of course it can be solved from a matching instance
in the function's context (which compiles to an a parameter for
KnownSNumber a n).
Bundled Patterns
| pattern SNumber :: forall (n :: Integer) a. SNumberRepr a => KnownSNumber a n => SNumber a n | Treat |
| pattern N# :: forall (n :: Integer) a. a -> SNumber a n | Create an This pattern is identical to the newtype constructor of |
| pattern SN :: forall (n :: Integer) a. a -> SNumber a n | A unidirectional pattern for matching This allows the pseudo-field-selector |
class Integral a => SNumberRepr a Source #
The class of types that are suitable for use as integer value witnesses.
Implementing an instance of this class amounts to asserting that the type
and its Integral, Ord, and Eq instances uphold the requirements of
SNumber, for any integer n that satisfies SafeSNumber a n.
Furthermore, it requires that every value of a is an integer, i.e. that
forall x :: a. exists y :: Integer. x == fromInteger y, toInteger x == y.
This ensures we can wrap any a in SomeSNumberVal and be sure it
corresponds to a valid K.Integer.
Associated Types
type SafeSNumber a :: Integer -> Constraint Source #
Instances
| SNumberRepr Int Source # | |
Defined in Data.SNumber Associated Types type SafeSNumber Int :: Integer -> Constraint Source # | |
| SNumberRepr Integer Source # | |
Defined in Data.SNumber Associated Types type SafeSNumber Integer :: Integer -> Constraint Source # | |
| SNumberRepr Natural Source # | |
Defined in Data.SNumber Associated Types type SafeSNumber Natural :: Integer -> Constraint Source # | |
| SNumberRepr Word Source # | |
Defined in Data.SNumber Associated Types type SafeSNumber Word :: Integer -> Constraint Source # | |
Creation
snumber :: forall n a. (SNumberRepr a, SafeSNumber a n, KnownInteger n) => SNumber a n Source #
Construct an SNumber, doing all validation at the type level.
This is completely safe and cannot raise runtime errors.
trySNumber :: forall n a. (SNumberRepr a, KnownInteger n) => Maybe (SNumber a n) Source #
Create an SNumber for n, if it's faithfully represented by a.
This is completely safe, but it may raise runtime errors, because it tests
at runtime whether a can represent n correctly.
Unsafe
unsafeUncheckedSNumber :: forall n a. (Num a, KnownInteger n) => SNumber a n Source #
Create an SNumber from a KnownInteger constraint without any checks.
This will cause type unsoundness if the value is not correctly represented
by a or if the instances of a do not behave according to the safety
requirements described on SNumber.
unsafeMkSNumber :: forall n a. SafeSNumber a n => a -> SNumber a n Source #
Semi-safely construct an SNumber as if by N#.
Callers must ensure that the a argument is fromInteger (integerVal n)
(semantically, not syntactically: unsafeSNumber ('Pos 42) 42 is fine and
is in fact the intended use case).
The extra constraint here compared to N# ensures that if you write
unsafeMkSNumber @n (fromInteger n), the result is either a valid
SNumber or a type error. In particular, the cases this rules out are
those where toInteger (fromInteger n :: a) /= n or where
fromInteger m :: a == fromInteger n does not imply m == n.
unsafeTryMkSNumber :: forall n a. SNumberRepr a => Integer -> Maybe (SNumber a n) Source #
Create an SNumber for n, if it's faithfully represented by a.
As with unsafeMkSNumber, this trusts you to pass integerVal @n, and
violating that will lead to type unsoundness.
This tests at runtime whether a represents n correctly.
unsafeUncheckedMkSNumber :: forall n a. a -> SNumber a n Source #
Create an SNumber for n, with no safety measures whatsoever.
Existentials
data SomeSNumber a Source #
Constructors
| forall n. SomeSNumber (SNumber a n) |
someSNumberVal :: SNumberRepr a => a -> SomeSNumber a Source #
Create an from any value of type SNumber aa.
Since SNumberRepr guarantees every a value is an integer, we can freely
wrap up a value in an SNumber with existential Integer type parameter.
withSNumber :: SNumberRepr a => a -> (forall n. SNumber a n -> r) -> r Source #
Like someSNumberVal, but in quantified CPS style rather than GADT style.
Comparison
compareSNumber :: forall m n a. Ord a => SNumber a m -> SNumber a n -> OrderingI m n Source #
Compare two type-level Integers using their runtime witnesses.
sameSNumber :: Eq a => SNumber a m -> SNumber a n -> Maybe (m :~: n) Source #
Test equality of two type-level Integers using their runtime witnesses.
Arithmetic
class Integral a => OverflowArith a where Source #
Arithmetic ops with overflow detection.
Laws:
overflowAdd x y = (False, xy) => toInteger xy === toInteger x + toInteger yoverflowSub x y = (False, xy) => toInteger xy === toInteger x - toInteger yoverflowMul x y = (False, xy) => toInteger xy === toInteger x * toInteger y
This is used for arithmetic on SNumber runtime values, so creating
incorrect instances is type-unsafe.
Methods
overflowAdd :: a -> a -> (Bool, a) Source #
overflowSub :: a -> a -> (Bool, a) Source #
overflowMul :: a -> a -> (Bool, a) Source #
In Maybe
tryAdd :: (SNumberRepr a, OverflowArith a) => SNumber a m -> SNumber a n -> Maybe (SNumber a (m + n)) Source #
Compute a runtime witness of m + n, or Nothing.
trySub :: (SNumberRepr a, OverflowArith a) => SNumber a m -> SNumber a n -> Maybe (SNumber a (m - n)) Source #
Compute a runtime witness of m - n, or Nothing.
tryMul :: (SNumberRepr a, OverflowArith a) => SNumber a m -> SNumber a n -> Maybe (SNumber a (m * n)) Source #
Compute a runtime witness of m * n, or Nothing.
Checked
data UnrepresentableSNumber Source #
An exception for overflows in checked SNumber arithmetic.
Constructors
| UnrepresentableSNumber String Integer Integer |
Instances
| Show UnrepresentableSNumber Source # | |
Defined in Data.SNumber Methods showsPrec :: Int -> UnrepresentableSNumber -> ShowS # show :: UnrepresentableSNumber -> String # showList :: [UnrepresentableSNumber] -> ShowS # | |
| Exception UnrepresentableSNumber Source # | |
Defined in Data.SNumber | |
chkAdd :: (SNumberRepr a, OverflowArith a, HasCallStack) => SNumber a m -> SNumber a n -> SNumber a (m + n) Source #
Compute a runtime witness of m + n, or throw.
chkSub :: (SNumberRepr a, OverflowArith a, HasCallStack) => SNumber a m -> SNumber a n -> SNumber a (m - n) Source #
Compute a runtime witness of m - n, or throw.
chkMul :: (SNumberRepr a, OverflowArith a, HasCallStack) => SNumber a m -> SNumber a n -> SNumber a (m * n) Source #
Compute a runtime witness of m * n, or throw.
Infallible
divExact :: SNumberRepr a => SNumber a (m * n) -> SNumber a n -> SNumber a m Source #
Compute a runtime witness of exact division.
We could provide division in terms of Div instead, but
Kinds.Integer doesn't currently have division.
Reification to Constraints
class KnownSNumber a n where Source #
Like KnownNat, but represented by a instead of Natural.
This is currently solved automatically from KnownNat via runtime checks,
to ease migration (we can incrementally strengthen KnownNat constraints to
KnownSNumber constraints), but in the future it may be changed to use
SafeSNumber, which will rule out runtime failures but will require
additional proofs in order to solve via KnownNat. For statically known
Integers, these proofs will be entirely automatic.
This class is meant to be used primarily in instance contexts; for
standalone functions, it's probably better to pass an SNumber explicitly.
Minimal complete definition
Nothing
Methods
snumberVal_ :: SNumber a n Source #
Implementation of snumberVal.
This has an inconvenient type variable order because it derives from the
order they appear in the class head; see snumberVal.
default snumberVal_ :: (SNumberRepr a, KnownInteger n) => SNumber a n Source #
Instances
| (SNumberRepr a, KnownInteger n) => KnownSNumber a n Source # | |
Defined in Data.SNumber Methods snumberVal_ :: SNumber a n Source # | |
snumberVal :: forall n a. KnownSNumber a n => SNumber a n Source #
Get an SNumber out of the context from KnownSNumber or KnownNat.
This has the number as its first type parameter, for TypeApplications convenience.
reifySNumber :: forall a n r. SNumber a n -> (KnownSNumber a n => r) -> r Source #
Stash an SNumber at the type level as a KnownSNumber instance.
reifySNumberAsNat :: forall n r a. Integral a => SNumber a ('Pos n) -> (KnownNat n => r) -> r Source #