typelits-witnesses 0.2.2.0 → 0.2.3.0
raw patch · 5 files changed
+257/−5 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- GHC.TypeLits.List: (:<#) :: !(Proxy n) -> !(NatList ns) -> NatList (n : ns)
- GHC.TypeLits.List: (:<$) :: !(Proxy s) -> !(SymbolList ss) -> SymbolList (s : ss)
- GHC.TypeLits.List: SomeNats :: !(NatList ns) -> SomeNats
- GHC.TypeLits.List: SomeSymbols :: !(SymbolList ss) -> SomeSymbols
- GHC.TypeLits.List: ØNL :: NatList '[]
- GHC.TypeLits.List: ØSL :: SymbolList '[]
+ GHC.TypeLits.Compare: (%<=?) :: (KnownNat m, KnownNat n) => Proxy m -> Proxy n -> (m :<=? n)
+ GHC.TypeLits.Compare: [CEQ] :: (CmpNat m n :~: EQ) -> SCmpNat m n
+ GHC.TypeLits.Compare: [CGT] :: (CmpNat m n :~: GT) -> SCmpNat m n
+ GHC.TypeLits.Compare: [CLT] :: (CmpNat m n :~: LT) -> SCmpNat m n
+ GHC.TypeLits.Compare: [LE] :: ((m <=? n) :~: True) -> (m :<=? n)
+ GHC.TypeLits.Compare: [NLE] :: ((m <=? n) :~: False) -> (m :<=? n)
+ GHC.TypeLits.Compare: cmpNat :: (KnownNat m, KnownNat n) => Proxy m -> Proxy n -> SCmpNat m n
+ GHC.TypeLits.Compare: cmpNatEq :: (CmpNat m n :~: EQ) -> (m :~: n)
+ GHC.TypeLits.Compare: cmpNatLE :: SCmpNat m n -> (m :<=? n)
+ GHC.TypeLits.Compare: data (:<=?) :: Nat -> Nat -> *
+ GHC.TypeLits.Compare: data SCmpNat :: Nat -> Nat -> *
+ GHC.TypeLits.Compare: eqCmpNat :: (m :~: n) -> (CmpNat m n :~: EQ)
+ GHC.TypeLits.Compare: flipCmpNat :: SCmpNat m n -> SCmpNat n m
+ GHC.TypeLits.Compare: isLE :: (KnownNat m, KnownNat n) => Proxy m -> Proxy n -> Maybe ((m <=? n) :~: True)
+ GHC.TypeLits.Compare: isNLE :: (KnownNat m, KnownNat n) => Proxy m -> Proxy n -> Maybe ((m <=? n) :~: False)
+ GHC.TypeLits.List: [:<#] :: (KnownNat n, KnownNats ns) => !(Proxy n) -> !(NatList ns) -> NatList (n : ns)
+ GHC.TypeLits.List: [:<$] :: (KnownSymbol s, KnownSymbols ss) => !(Proxy s) -> !(SymbolList ss) -> SymbolList (s : ss)
+ GHC.TypeLits.List: [SomeNats] :: KnownNats ns => !(NatList ns) -> SomeNats
+ GHC.TypeLits.List: [SomeSymbols] :: KnownSymbols ss => !(SymbolList ss) -> SomeSymbols
+ GHC.TypeLits.List: [ØNL] :: NatList '[]
+ GHC.TypeLits.List: [ØSL] :: SymbolList '[]
+ GHC.TypeLits.Witnesses: infixl 6 %-
+ GHC.TypeLits.Witnesses: infixl 7 %*
+ GHC.TypeLits.Witnesses: infixr 8 %^
- GHC.TypeLits.List: elimNatList :: p '[] -> (forall m ms. (KnownNat m, KnownNats ms) => Proxy m -> p ms -> p (m : ms)) -> NatList ns -> p ns
+ GHC.TypeLits.List: elimNatList :: forall p ns. p '[] -> (forall m ms. (KnownNat m, KnownNats ms) => Proxy m -> p ms -> p (m : ms)) -> NatList ns -> p ns
- GHC.TypeLits.List: elimSymbolList :: p '[] -> (forall t ts. (KnownSymbol t, KnownSymbols ts) => Proxy t -> p ts -> p (t : ts)) -> SymbolList ss -> p ss
+ GHC.TypeLits.List: elimSymbolList :: forall p ss. p '[] -> (forall t ts. (KnownSymbol t, KnownSymbols ts) => Proxy t -> p ts -> p (t : ts)) -> SymbolList ss -> p ss
- GHC.TypeLits.List: traverseNatList :: Applicative f => (forall n. KnownNat n => Proxy n -> f SomeNat) -> NatList ns -> f SomeNats
+ GHC.TypeLits.List: traverseNatList :: forall f ns. Applicative f => (forall n. KnownNat n => Proxy n -> f SomeNat) -> NatList ns -> f SomeNats
- GHC.TypeLits.List: traverseNatList' :: Applicative f => (SomeNat -> f SomeNat) -> SomeNats -> f SomeNats
+ GHC.TypeLits.List: traverseNatList' :: forall f. Applicative f => (SomeNat -> f SomeNat) -> SomeNats -> f SomeNats
- GHC.TypeLits.List: traverseNatList_ :: Applicative f => (forall n. KnownNat n => Proxy n -> f a) -> NatList ns -> f ()
+ GHC.TypeLits.List: traverseNatList_ :: forall f a ns. Applicative f => (forall n. KnownNat n => Proxy n -> f a) -> NatList ns -> f ()
- GHC.TypeLits.List: traverseSymbolList :: Applicative f => (forall s. KnownSymbol s => Proxy s -> f SomeSymbol) -> SymbolList ss -> f SomeSymbols
+ GHC.TypeLits.List: traverseSymbolList :: forall f ss. Applicative f => (forall s. KnownSymbol s => Proxy s -> f SomeSymbol) -> SymbolList ss -> f SomeSymbols
- GHC.TypeLits.List: traverseSymbolList' :: Applicative f => (SomeSymbol -> f SomeSymbol) -> SomeSymbols -> f SomeSymbols
+ GHC.TypeLits.List: traverseSymbolList' :: forall f. Applicative f => (SomeSymbol -> f SomeSymbol) -> SomeSymbols -> f SomeSymbols
- GHC.TypeLits.List: traverseSymbolList_ :: Applicative f => (forall s a. KnownSymbol s => Proxy s -> f a) -> SymbolList ss -> f ()
+ GHC.TypeLits.List: traverseSymbolList_ :: forall f ss. Applicative f => (forall s a. KnownSymbol s => Proxy s -> f a) -> SymbolList ss -> f ()
- GHC.TypeLits.Witnesses: (%*) :: Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n * m))
+ GHC.TypeLits.Witnesses: (%*) :: forall n m. Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n * m))
- GHC.TypeLits.Witnesses: (%+) :: Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n + m))
+ GHC.TypeLits.Witnesses: (%+) :: forall n m. Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n + m))
- GHC.TypeLits.Witnesses: (%-) :: Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n - m))
+ GHC.TypeLits.Witnesses: (%-) :: forall n m. Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n - m))
- GHC.TypeLits.Witnesses: (%^) :: Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n ^ m))
+ GHC.TypeLits.Witnesses: (%^) :: forall n m. Dict (KnownNat n) -> Dict (KnownNat m) -> Dict (KnownNat (n ^ m))
- GHC.TypeLits.Witnesses: dictNatVal :: Dict (KnownNat n) -> Integer
+ GHC.TypeLits.Witnesses: dictNatVal :: forall n. Dict (KnownNat n) -> Integer
- GHC.TypeLits.Witnesses: entailAdd :: (KnownNat n, KnownNat m) :- KnownNat (n + m)
+ GHC.TypeLits.Witnesses: entailAdd :: forall n m. (KnownNat n, KnownNat m) :- KnownNat (n + m)
- GHC.TypeLits.Witnesses: entailExp :: (KnownNat n, KnownNat m) :- KnownNat (n ^ m)
+ GHC.TypeLits.Witnesses: entailExp :: forall n m. (KnownNat n, KnownNat m) :- KnownNat (n ^ m)
- GHC.TypeLits.Witnesses: entailMul :: (KnownNat n, KnownNat m) :- KnownNat (n * m)
+ GHC.TypeLits.Witnesses: entailMul :: forall n m. (KnownNat n, KnownNat m) :- KnownNat (n * m)
- GHC.TypeLits.Witnesses: entailSub :: (KnownNat n, KnownNat m) :- KnownNat (n - m)
+ GHC.TypeLits.Witnesses: entailSub :: forall n m. (KnownNat n, KnownNat m) :- KnownNat (n - m)
Files
- CHANGELOG.md +8/−0
- README.md +72/−3
- src/GHC/TypeLits/Compare.hs +159/−0
- src/GHC/TypeLits/List.hs +4/−0
- typelits-witnesses.cabal +14/−2
CHANGELOG.md view
@@ -1,3 +1,11 @@+Version 0.2.3.0+===============++<https://github.com/mstksg/typelits-witnesses/releases/tag/v0.2.3.0>++* Added the `GHC.TypeLits.Compare` module for refining bounds and proving+ inequalities on `KnownNat`s and associated utility functions.+ Version 0.2.2.0 ===============
README.md view
@@ -1,11 +1,11 @@ typelits-witnesses ================== -[](http://stackage.org/lts-2/package/typelits-witnesses)-[](http://stackage.org/lts-3/package/typelits-witnesses)-[](http://stackage.org/lts-4/package/typelits-witnesses)+[](https://hackage.haskell.org/package/typelits-witnesses) [](http://stackage.org/lts/package/typelits-witnesses) [](http://stackage.org/nightly/package/typelits-witnesses)+[](https://travis-ci.org/mstksg/typelits-witnesses) Provides witnesses for `KnownNat` and `KnownSymbol` instances for various@@ -21,6 +21,16 @@ able to apply analogies of `natVal`/`symbolVal` to lists with analogies for `SomeNat` and `SomeSymbol`. +Note that most of the functionality in this library can be reproduced in a more+generic way using the great [singletons][] library. The versions here are+provided as a "plumbing included" alternative that makes some commonly found+design patterns involving GHC's TypeLits functionality a little smoother,+especially when working with external libraries or GHC TypeLit's `Nat`+comparison API.++[singletons]: https://hackage.haskell.org/package/singletons++ `GHC.TypeLits.Witnesses` ------------------------ @@ -83,6 +93,65 @@ There are more advanced operations dealing with low-level machinery, as well, in the module. See module documentation for more detail.++`GHC.TypeLits.Compare`+----------------------++Provides tools for refining upper and lower bounds on `KnownNat`s and proving+inequalities involving *GHC.TypeLits*'s comparison API. (Both with `<=?` and+`CmpNat`).++If a library function requires `1 <= n` constraint, but only `KnownNat n` is+available:++~~~haskell+foo :: (KnownNat n, 1 <= n) => Proxy n -> Int++bar :: KnownNat n => Proxy n -> Int+bar n = case (Proxy :: Proxy 1) %<=? n of+ LE Refl -> foo n+ NLE _ -> 0+~~~++`foo` requires that `1 <= n`, but `bar` has to handle all cases of `n`. `%<=?`+lets you compare the `KnownNat`s in two `Proxy`s and returns a `:<=?`, which+has two constructors, `LE` and `NLE`.++If you pattern match on the result, in the `LE` branch, the constraint+`1 <= n` will be satisfied according to GHC, so `bar` can safely call+`foo`, and GHC will recognize that `1 <= n`.++In the `NLE` branch, the constraint that `1 > n` is satisfied, so any+functions that require that constraint would be callable.++For convenience, `isLE` and `isNLE` are also offered:++~~~haskell+bar :: KnownNat n => Proxy n -> Int+bar n = case isLE (Proxy :: Proxy 1) n of+ Just Refl -> foo n+ Nothing -> 0+~~~++Similarly, if a library function requires something involving `CmpNat`,+you can use `cmpNat` and the `SCmpNat` type:++~~~haskell+foo1 :: (KnownNat n, CmpNat 5 n ~ LT) => Proxy n -> Int+foo2 :: (KnownNat n, CmpNat 5 n ~ GT) => Proxy n -> Int++bar :: KnownNat n => Proxy n -> Int+bar n = case cmpNat (Proxy :: Proxy 5) n of+ CLT Refl -> foo1 n+ CEQ Refl -> 0+ CGT Refl -> foo2 n+~~~++You can use the `Refl` that `cmpNat` gives you with `flipCmpNat` and+`cmpNatLE` to "flip" the inequality or turn it into something compatible+with `<=?` (useful for when you have to work with libraries that mix the+two methods) or `cmpNatEq` and `eqCmpNat` to get to/from witnesses for+equality of the two `Nat`s. `GHC.TypeLits.List` -------------------
+ src/GHC/TypeLits/Compare.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++-- |+-- Module : GHC.TypeLits.Compare+-- Description : Tools and singletons for proving and refining inequalities+-- and bounds on GHC TypeLits types using '<=' and '<=?'+-- Copyright : (c) Justin Le 2016+-- License : MIT+-- Maintainer : justin@jle.im+-- Stability : unstable+-- Portability : non-portable+--+--+-- This module provides the ability to refine given 'KnownNat' instances+-- using "GHC.TypeLits"'s comparison API, and also the ability to prove+-- inequalities and upper/lower limits.+--+-- If a library function requires @1 '<=' n@ constraint, but only+-- @'KnownNat' n@ is available:+--+-- @+-- foo :: (KnownNat n, 1 '<=' n) => 'Proxy' n -> Int+--+-- bar :: KnownNat n => Proxy n -> Int+-- bar n = case (Proxy :: Proxy 1) '%<=?' n of+-- 'LE' 'Refl' -> foo n+-- 'NLE' _ -> 0+-- @+--+-- @foo@ requires that @1 <= n@, but @bar@ has to handle all cases of @n@.+-- @%<=?@ lets you compare the 'KnownNat's in two 'Proxy's and returns+-- a @:<=?@, which has two constructors, 'LE' and 'NLE'.+--+-- If you pattern match on the result, in the 'LE' branch, the constraint+-- @1 <= n@ will be satisfied according to GHC, so @bar@ can safely call+-- @foo@, and GHC will recognize that @1 <= n@.+--+-- In the 'NLE' branch, the constraint that @1 > n@ is satisfied, so any+-- functions that require that constraint would be callable.+--+-- For convenience, 'isLE' and 'isNLE' are also offered:+--+-- @+-- bar :: KnownNat n => Proxy n -> Int+-- bar n = case 'isLE' (Proxy :: Proxy 1) n of+-- 'Just' Refl -> foo n+-- 'Nothing' -> 0+-- @+--+-- Similarly, if a library function requires something involving 'CmpNat',+-- you can use 'cmpNat' and the 'SCmpNat' type:+--+-- @+-- foo1 :: (KnownNat n, 'CmpNat' 5 n ~ LT) => Proxy n -> Int+-- foo2 :: (KnownNat n, CmpNat 5 n ~ GT) => Proxy n -> Int+--+-- bar :: KnownNat n => Proxy n -> Int+-- bar n = case 'cmpNat' (Proxy :: Proxy 5) n of+-- 'CLT' Refl -> foo1 n+-- 'CEQ' Refl -> 0+-- 'CGT' Refl -> foo2 n+-- @+--+-- You can use the 'Refl' that 'cmpNat' gives you with 'flipCmpNat' and+-- 'cmpNatLE' to "flip" the inequality or turn it into something compatible+-- with '<=?' (useful for when you have to work with libraries that mix the+-- two methods) or 'cmpNatEq' and 'eqCmpNat' to get to/from witnesses for+-- equality of the two 'Nat's.++module GHC.TypeLits.Compare+ ( -- * '<=' and '<=?'+ (:<=?)(..)+ , (%<=?)+ -- ** Convenience functions+ , isLE+ , isNLE+ -- * 'CmpNat'+ , SCmpNat(..)+ , cmpNat+ -- ** Manipulating witnesses+ , flipCmpNat+ , cmpNatEq+ , eqCmpNat+ -- ** Interfacing with '<=?'+ , cmpNatLE+ )+ where++import Data.Type.Equality+import Data.Proxy+import GHC.TypeLits+import Unsafe.Coerce++isLE+ :: (KnownNat m, KnownNat n)+ => Proxy m+ -> Proxy n+ -> Maybe ((m <=? n) :~: 'True)+isLE m n = case m %<=? n of+ LE Refl -> Just Refl+ NLE _ -> Nothing++isNLE+ :: (KnownNat m, KnownNat n)+ => Proxy m+ -> Proxy n+ -> Maybe ((m <=? n) :~: 'False)+isNLE m n = case m %<=? n of+ NLE Refl -> Just Refl+ LE _ -> Nothing++data (:<=?) :: Nat -> Nat -> * where+ LE :: ((m <=? n) :~: 'True) -> (m :<=? n)+ NLE :: ((m <=? n) :~: 'False) -> (m :<=? n)++(%<=?)+ :: (KnownNat m, KnownNat n)+ => Proxy m+ -> Proxy n+ -> (m :<=? n)+m %<=? n | natVal m <= natVal n = LE (unsafeCoerce Refl)+ | otherwise = NLE (unsafeCoerce Refl)++data SCmpNat :: Nat -> Nat -> * where+ CLT :: (CmpNat m n :~: 'LT) -> SCmpNat m n+ CEQ :: (CmpNat m n :~: 'EQ) -> SCmpNat m n+ CGT :: (CmpNat m n :~: 'GT) -> SCmpNat m n++cmpNat+ :: (KnownNat m, KnownNat n)+ => Proxy m+ -> Proxy n+ -> SCmpNat m n+cmpNat m n = case compare (natVal m) (natVal n) of+ LT -> CLT (unsafeCoerce Refl)+ EQ -> CEQ (unsafeCoerce Refl)+ GT -> CGT (unsafeCoerce Refl)++flipCmpNat :: SCmpNat m n -> SCmpNat n m+flipCmpNat = \case CLT Refl -> CGT (unsafeCoerce Refl)+ CEQ Refl -> CEQ (unsafeCoerce Refl)+ CGT Refl -> CLT (unsafeCoerce Refl)++cmpNatEq :: (CmpNat m n :~: 'EQ) -> (m :~: n)+cmpNatEq = \case Refl -> unsafeCoerce Refl++eqCmpNat :: (m :~: n) -> (CmpNat m n :~: 'EQ)+eqCmpNat = \case Refl -> unsafeCoerce Refl++cmpNatLE :: SCmpNat m n -> (m :<=? n)+cmpNatLE = \case CLT Refl -> LE (unsafeCoerce Refl)+ CEQ Refl -> LE (unsafeCoerce Refl)+ CGT Refl -> NLE (unsafeCoerce Refl)
src/GHC/TypeLits/List.hs view
@@ -172,6 +172,8 @@ -- | The "eliminator" for 'NatList'. You can think of this as -- a dependently typed analogy for a fold.+--+-- /Since 0.2.1.0/ elimNatList :: forall p ns. () => p '[]@@ -385,6 +387,8 @@ -- | The "eliminator" for 'SymbolList'. You can think of this as -- a dependently typed analogy for a fold.+--+-- /Since 0.2.1.0/ elimSymbolList :: forall p ss. () => p '[]
typelits-witnesses.cabal view
@@ -1,5 +1,5 @@ name: typelits-witnesses-version: 0.2.2.0+version: 0.2.3.0 synopsis: Existential witnesses, singletons, and classes for operations on GHC TypeLits description: Provides witnesses for 'KnownNat' and 'KnownSymbol' instances for various operations on GHC TypeLits - in@@ -9,7 +9,9 @@ . This is useful for situations where you have @'KnownNat' n@, and you want to prove to GHC- @'KnownNat' (n + 3)@, or @'KnownNat' (2*n + 4)@.+ @'KnownNat' (n + 3)@, or @'KnownNat' (2*n + 4)@, or when+ constraints on the lower or upper limits of @n@ are+ needed. . It's also useful for when you want to work with type level lists of 'KnownNat' or 'KnownSymbol' instances and@@ -17,6 +19,15 @@ analogies of 'natVal' and 'symbolVal' to lists with analogies for 'SomeNat' and 'SomeSymbol'. .+ Note that most of the functionality in this library can+ be reproduced in a more generic way using the great+ <https://hackage.haskell.org/package/singletons singletons>+ library. The versions here are provided as a "plumbing+ included" alternative that makes some commonly found+ design patterns involving GHC's TypeLits functionality a+ little smoother, especially when working with external+ libraries or GHC TypeLit's Nat comparison API.+ . See README for more information. homepage: https://github.com/mstksg/typelits-witnesses license: MIT@@ -37,6 +48,7 @@ library exposed-modules: GHC.TypeLits.Witnesses GHC.TypeLits.List+ GHC.TypeLits.Compare -- other-modules: -- other-extensions: build-depends: base >=4.7 && <5