packages feed

kind-integer 0.3 → 0.4

raw patch · 4 files changed

+128/−18 lines, 4 filesdep +singletonsdep −ghc-primPVP ok

version bump matches the API change (PVP)

Dependencies added: singletons

Dependencies removed: ghc-prim

API changes (from Hackage documentation)

+ KindInteger: eqIntegerRep :: Integer -> Integer -> Bool
+ KindInteger: fromSInteger' :: SInteger i -> Integer
+ KindInteger: instance Data.Singletons.Decide.SDecide KindInteger.Integer

Files

CHANGELOG.md view
@@ -1,3 +1,22 @@+# Version 0.4++* COMPILER ASSISTED BREAKING CHANGE: `TestEquality` and `TestCoercion` consider+  `N 0` and `P 0` to be different.++* BREAKING CHANGE: The `Integer` inside `SInteger`s is not automatically+  normalized anymore. This is so that `SDecide`, `TestEquality` and+  `TestCoercion` behave as expected, treating `N 0` and `P 0` differently.+  This is mostly an internal change, but it can be observed in the `Show`+  instance for `SInteger`, for example.++* Added role annotations to `SInteger`.++* Add dependency on `singletons` so that we can give a `Sing` and `SDecide`+  instances for type-level `Integer`s.++* Export `fromSInteger'`, `eqIntegerRep`.++ # Version 0.3  * COMPILER ASSISTED BREAKING CHANGE: Renamed `Mod` to `Rem`, `DivMod` to
kind-integer.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: kind-integer-version: 0.3+version: 0.4 license: BSD-3-Clause license-file: LICENSE extra-source-files: README.md CHANGELOG.md@@ -28,6 +28,7 @@     DataKinds     MultiWayIf     NoStarIsType+    RoleAnnotations     PatternSynonyms     TypeFamilies     TypeOperators@@ -35,8 +36,8 @@  library   import: basic+  build-depends: singletons   hs-source-dirs: lib-  build-depends: ghc-prim   exposed-modules: KindInteger  test-suite test
lib/KindInteger.hs view
@@ -34,6 +34,7 @@   , SInteger   , pattern SInteger   , fromSInteger+  , fromSInteger'   , withSomeSInteger   , withKnownInteger @@ -54,6 +55,7 @@     -- * Comparisons   , CmpInteger   , cmpInteger+  , eqIntegerRep      -- * Extra   , type (==?), type (==), type (/=?), type (/=)@@ -63,15 +65,17 @@ import Control.Exception qualified as Ex import Data.Bits import Data.Proxy+import Data.Singletons+import Data.Singletons.Decide import Data.Type.Bool (If) import Data.Type.Coercion-import Data.Type.Equality (TestEquality(..), (:~:)(..))+import Data.Type.Equality (TestEquality(..)) import Data.Type.Ord import GHC.Base (WithDict(..))+import GHC.Exts (TYPE, Constraint) import GHC.Real qualified as P import GHC.Show (appPrec, appPrec1) import GHC.TypeLits qualified as L-import GHC.Types (TYPE, Constraint) import Numeric.Natural (Natural) import Prelude hiding (Integer, (==), (/=), div, rem) import Prelude qualified as P@@ -179,15 +183,21 @@  -- | Positive numbers and zero. instance L.KnownNat x => KnownInteger (P x) where-  integerSing = UnsafeSInteger (L.natVal (Proxy @x))+  integerSing = UnsafeSInteger (P_ (fromInteger (L.natVal (Proxy @x))))  -- | Negative numbers and zero.+--+-- Implementation note: Notice that @'N' 0@ will not be 'Normalize'd to+-- @'P' 0@. This is so that 'SDecide', 'TestEquality' and 'TestCoercion'+-- behave as expected. If you want a 'Normalize'd 'SInteger', then use+-- @'integerSing' \@('Normalize' i)@. instance L.KnownNat x => KnownInteger (N x) where-  integerSing = UnsafeSInteger (negate (L.natVal (Proxy @x)))+  integerSing = UnsafeSInteger (N_ (fromInteger (L.natVal (Proxy @x))))  -- | Term-level 'P.Integer' representation of the type-level 'Integer' @i@. integerVal :: forall i proxy. KnownInteger i => proxy i -> P.Integer-integerVal _ = case integerSing :: SInteger i of UnsafeSInteger x -> x+integerVal _ = case integerSing :: SInteger i of+                 UnsafeSInteger x -> toPrelude x  -- | This type represents unknown type-level 'Integer'. data SomeInteger = forall n. KnownInteger n => SomeInteger (Proxy n)@@ -464,7 +474,8 @@ --------------------------------------------------------------------------------  -- | Singleton type for a type-level 'Integer' @i@.-newtype SInteger (i :: Integer) = UnsafeSInteger P.Integer+newtype SInteger (i :: Integer) = UnsafeSInteger Integer+type role SInteger representational  -- | A explicitly bidirectional pattern synonym relating an 'SInteger' to a -- 'KnownInteger' constraint.@@ -499,22 +510,44 @@  instance Show (SInteger i) where   showsPrec p (UnsafeSInteger i) = showParen (p > appPrec) $-    showString "SInteger @" .-    showsPrec appPrec1 (fromPrelude i)+    showString "SInteger @" . showsPrecTypeLit appPrec1 i +-- | Note that this checks for type equality. That is, @'P' 0@ and @'N' 0@+-- are not equal types, even if they are treated equally elsewhere in+-- "KindInteger". instance TestEquality SInteger where-  testEquality (UnsafeSInteger x) (UnsafeSInteger y)-    | x P.== y  = Just (unsafeCoerce Refl)-    | otherwise = Nothing+  testEquality = decideEquality+  {-# INLINE testEquality #-} +-- | Note that this checks for type equality. That is, @'P' 0@ and @'N' 0@+-- are not equal types, even if they are treated equally elsewhere in+-- "KindInteger". instance TestCoercion SInteger where-  testCoercion x y = fmap (\Refl -> Coercion) (testEquality x y)+  testCoercion = decideCoercion+  {-# INLINE testCoercion #-} --- | Return the term-level 'P.Integer' number corresponding to @i@ in--- a @'SInteger' i@ value.+-- | Return the term-level "Prelude" 'P.Integer' number corresponding to @i@+-- in a @'SInteger' i@ value. fromSInteger :: SInteger i -> P.Integer-fromSInteger (UnsafeSInteger i) = i+fromSInteger (UnsafeSInteger i) = toPrelude i+{-# INLINE fromSInteger #-} +-- | Return the term-level "KindInteger" 'Integer' number corresponding to @i@+-- in a @'SInteger' i@ value.+fromSInteger' :: SInteger i -> Integer+fromSInteger' (UnsafeSInteger i) = i+{-# INLINE fromSInteger' #-}++-- | Whether the internal representation of the 'Integer's are equal.+--+-- Note that this is not the same as '(==)'. Use '(==)' unless you+-- know what you are doing.+eqIntegerRep :: Integer -> Integer -> Bool+eqIntegerRep (N_ l) (N_ r) = l P.== r+eqIntegerRep (P_ l) (P_ r) = l P.== r+eqIntegerRep _      _      = False+{-# INLINE eqIntegerRep #-}+ -- | Convert an explicit @'SInteger' i@ value into an implicit -- @'KnownInteger' i@ constraint. withKnownInteger@@ -525,9 +558,22 @@ -- fresh type-level 'Integer'. withSomeSInteger   :: forall rep (r :: TYPE rep). P.Integer -> (forall n. SInteger n -> r) -> r-withSomeSInteger n k = k (UnsafeSInteger n)+withSomeSInteger n k = k (UnsafeSInteger (fromPrelude n)) -- It's very important to keep this NOINLINE! See the docs at "GHC.TypeNats" {-# NOINLINE withSomeSInteger #-}++--------------------------------------------------------------------------------++type instance Sing = SInteger++-- | Note that this checks for type equality. That is, @'P' 0@ and @'N' 0@+-- are not equal types, even if they are treated equally elsewhere in+-- "KindInteger".+instance SDecide Integer where+  UnsafeSInteger l %~ UnsafeSInteger r =+    case eqIntegerRep l r of+      True  -> Proved (unsafeCoerce Refl)+      False -> Disproved (\Refl -> error "SDecide.Integer")  -------------------------------------------------------------------------------- 
test/Main.hs view
@@ -9,6 +9,7 @@ import Data.List qualified as List import Data.Maybe import Data.Ratio as P+import Data.Type.Equality (TestEquality(..)) import Data.Type.Ord (type (<=)) import GHC.Exts (Constraint) import Prelude hiding (Integer)@@ -366,6 +367,49 @@       in readMaybe @P.Integer str             == fmap (\(K.SomeInteger p) -> K.integerVal p)                     (readMaybe @K.SomeInteger str)++  , assert "TestEquality +0 +0" $+     isJust (testEquality (K.SInteger @(P 0)) (K.SInteger @(P 0)))+  , assert "TestEquality -0 -0" $+     isJust (testEquality (K.SInteger @(N 0)) (K.SInteger @(N 0)))+  , assert "TestEquality +0 -0" $+     isNothing (testEquality (K.SInteger @(P 0)) (K.SInteger @(N 0)))+  , assert "TestEquality -0 +0" $+     isNothing (testEquality (K.SInteger @(N 0)) (K.SInteger @(P 0)))+  , assert "TestEquality +0 +1" $+     isNothing (testEquality (K.SInteger @(P 0)) (K.SInteger @(P 1)))+  , assert "TestEquality +0 -1" $+     isNothing (testEquality (K.SInteger @(P 0)) (K.SInteger @(N 1)))+  , assert "TestEquality -0 +1" $+     isNothing (testEquality (K.SInteger @(N 0)) (K.SInteger @(P 1)))+  , assert "TestEquality -0 -1" $+     isNothing (testEquality (K.SInteger @(N 0)) (K.SInteger @(N 1)))+  , assert "TestEquality +1 +0" $+     isNothing (testEquality (K.SInteger @(P 1)) (K.SInteger @(P 0)))+  , assert "TestEquality +1 -0" $+     isNothing (testEquality (K.SInteger @(P 1)) (K.SInteger @(N 0)))+  , assert "TestEquality -1 +0" $+     isNothing (testEquality (K.SInteger @(N 1)) (K.SInteger @(P 0)))+  , assert "TestEquality -1 -0" $+     isNothing (testEquality (K.SInteger @(N 1)) (K.SInteger @(N 0)))++  , assert "Show Integer +0" $+     "0" == show (K.fromSInteger (K.SInteger @(P 0)))+  , assert "Show Integer -0" $+     "0" == show (K.fromSInteger (K.SInteger @(N 0)))+  , assert "Show Integer +1" $+     "1" == show (K.fromSInteger (K.SInteger @(P 1)))+  , assert "Show Integer -1" $+     "-1" == show (K.fromSInteger (K.SInteger @(N 1)))++  , assert "Show SInteger +0" $+     "SInteger @(P 0)" == show (K.SInteger @(P 0))+  , assert "Show SInteger -0" $+     "SInteger @(N 0)" == show (K.SInteger @(N 0))+  , assert "Show SInteger +1" $+     "SInteger @(P 1)" == show (K.SInteger @(P 1))+  , assert "Show SInteger -1" $+     "SInteger @(N 1)" == show (K.SInteger @(N 1))    ] <> testsDivRem