packages feed

ghc-typelits-extra 0.3 → 0.3.1

raw patch · 7 files changed

+165/−41 lines, 7 filesdep +containersdep −template-haskelldep ~ghcdep ~ghc-typelits-knownnatdep ~ghc-typelits-natnormalisePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: containers

Dependencies removed: template-haskell

Dependency ranges changed: ghc, ghc-typelits-knownnat, ghc-typelits-natnormalise

API changes (from Hackage documentation)

- GHC.TypeLits.Extra: instance (GHC.TypeNats.KnownNat x, GHC.TypeNats.KnownNat y, GHC.TypeLits.Extra.FLog x y ~ GHC.TypeLits.Extra.CLog x y) => GHC.TypeLits.KnownNat.KnownNat2 "GHC.TypeLits.Extra.Log" x y
+ GHC.TypeLits.Extra: infixl 7 `Mod`
+ GHC.TypeLits.Extra: instance (GHC.TypeNats.KnownNat x, GHC.TypeNats.KnownNat y, GHC.TypeLits.Extra.FLog x y GHC.Types.~ GHC.TypeLits.Extra.CLog x y) => GHC.TypeLits.KnownNat.KnownNat2 "GHC.TypeLits.Extra.Log" x y
+ GHC.TypeLits.Extra: type family LCM (x :: Nat) (y :: Nat) :: Nat

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # Changelog for the [`ghc-typelits-extra`](http://hackage.haskell.org/package/ghc-typelits-extra) package +# 0.3.1 *August 26th 2019*+* Reduce `a <=? Max a b` to `True`+* Reduce `n ~ (Max a b) => a <=? n` to `True`+* Prove `Max (1 + n) 1 ~ (n+1)`+ # 0.3 *September 14th 2018* * Move `KnownNat2` instances for GHC 8.4's `Div` and `Mod` from `ghc-typelits-extra` to `ghc-typelits-knownnat` 
ghc-typelits-extra.cabal view
@@ -1,5 +1,5 @@ name:                ghc-typelits-extra-version:             0.3+version:             0.3.1 synopsis:            Additional type-level operations on GHC.TypeLits.Nat description:   Additional type-level operations on @GHC.TypeLits.Nat@:@@ -47,8 +47,7 @@ extra-source-files:  README.md                      CHANGELOG.md cabal-version:       >=1.10-tested-with:         GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1, GHC == 8.4.2,-                     GHC == 8.6+tested-with:         GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.4  source-repository head   type: git@@ -66,11 +65,12 @@   other-modules:       GHC.TypeLits.Extra.Solver.Unify                        GHC.TypeLits.Extra.Solver.Operations   build-depends:       base                      >= 4.8     && <5,-                       ghc                       >= 7.10    && <8.8,+                       containers                >= 0.5.7.1 && <0.7,+                       ghc                       >= 7.10    && <8.9,                        ghc-prim                  >= 0.5     && <1.0,                        ghc-tcplugins-extra       >= 0.2,-                       ghc-typelits-knownnat     >= 0.6     && <0.7,-                       ghc-typelits-natnormalise >= 0.6     && <0.7,+                       ghc-typelits-knownnat     >= 0.6     && <0.8,+                       ghc-typelits-natnormalise >= 0.6     && <0.8,                        integer-gmp               >= 1.0     && <1.1,                        transformers              >= 0.4.2.0 && <0.6   hs-source-dirs:      src@@ -101,12 +101,10 @@                        ghc-typelits-knownnat     >= 0.6,                        ghc-typelits-natnormalise >= 0.4.1,                        tasty                     >= 0.10,-                       tasty-hunit               >= 0.9,-                       template-haskell          >= 2.11.0.0+                       tasty-hunit               >= 0.9   hs-source-dirs:      tests   default-language:    Haskell2010   other-extensions:    DataKinds-                       TemplateHaskell                        TypeOperators   if flag(deverror)     ghc-options:       -O0 -dcore-lint
src/GHC/TypeLits/Extra/Solver.hs view
@@ -23,8 +23,8 @@ where  -- external-import Control.Monad             ((<=<)) import Control.Monad.Trans.Maybe (MaybeT (..))+import Data.Either               (lefts) import Data.Maybe                (catMaybes) import GHC.TcPluginM.Extra       (evByFiat, lookupModule, lookupName,                                   tracePlugin)@@ -39,9 +39,9 @@ import Plugins    (purePlugin) #endif import TcEvidence (EvTerm)-import TcPluginM  (TcPluginM, tcLookupTyCon, tcPluginTrace, zonkCt)+import TcPluginM  (TcPluginM, tcLookupTyCon, tcPluginTrace) import TcRnTypes  (Ct, TcPlugin(..), TcPluginResult (..), ctEvidence, ctEvPred,-                   isWanted)+                   isWantedCt) import TcType      (typeKind) import Type       (EqRel (NomEq), Kind, PredTree (EqPred), classifyPredType,                    eqType)@@ -49,7 +49,11 @@ import TysWiredIn (typeNatKind, promotedTrueDataCon, promotedFalseDataCon) import TcTypeNats (typeNatLeqTyCon) #if MIN_VERSION_ghc(8,4,0)+import GHC.TcPluginM.Extra (flattenGivens) import TcTypeNats (typeNatTyCons)+#else+import TcPluginM  (zonkCt)+import Control.Monad ((<=<)) #endif  -- internal@@ -102,16 +106,20 @@ decideEqualSOP _    _givens _deriveds []      = return (TcPluginOk [] []) decideEqualSOP defs givens  _deriveds wanteds = do   -- GHC 7.10.1 puts deriveds with the wanteds, so filter them out-  let wanteds' = filter (isWanted . ctEvidence) wanteds+  let wanteds' = filter isWantedCt wanteds   unit_wanteds <- catMaybes <$> mapM (runMaybeT . toNatEquality defs) wanteds'   case unit_wanteds of     [] -> return (TcPluginOk [] [])     _  -> do+#if MIN_VERSION_ghc(8,4,0)+      unit_givens <- catMaybes <$> mapM (runMaybeT . toNatEquality defs) (givens ++ flattenGivens givens)+#else       unit_givens <- catMaybes <$> mapM ((runMaybeT . toNatEquality defs) <=< zonkCt) givens+#endif       sr <- simplifyExtra (unit_givens ++ unit_wanteds)       tcPluginTrace "normalised" (ppr sr)       case sr of-        Simplified evs -> return (TcPluginOk (filter (isWanted . ctEvidence . snd) evs) [])+        Simplified evs -> return (TcPluginOk (filter (isWantedCt . snd) evs) [])         Impossible eq  -> return (TcPluginContradiction [fromNatEquality eq])  type NatEquality   = (Ct,ExtraOp,ExtraOp)@@ -145,7 +153,30 @@         (I i,I j)           | (i <= j) == b -> simples (((,) <$> evMagic ct <*> pure ct):evs) eqs'           | otherwise     -> return  (Impossible eq)+        (p, Max x y)+          | b && (p == x || p == y) -> simples (((,) <$> evMagic ct <*> pure ct):evs) eqs'++        -- transform:  q ~ Max x y => (p <=? q ~ True)+        -- to:         (p <=? Max x y) ~ True+        -- and try to solve that along with the rest of the eqs'+        (p, q@(V _))+          | b -> case findMax q eqs of+                   Just m  -> simples evs ((Right (ct,p,m,b)):eqs')+                   Nothing -> simples evs eqs'         _ -> simples evs eqs'++    -- look for given constraint with the form: c ~ Max x y+    findMax :: ExtraOp -> [Either NatEquality NatInEquality] -> Maybe ExtraOp+    findMax c = go . lefts+      where+        go [] = Nothing+        go ((ct, a,b@(Max _ _)) :_)+          | c == a && not (isWantedCt ct)+            = Just b+        go ((ct, a@(Max _ _),b) :_)+          | c == b && not (isWantedCt ct)+            = Just a+        go (_:rest) = go rest   -- Extract the Nat equality constraints
src/GHC/TypeLits/Extra/Solver/Operations.hs view
@@ -5,6 +5,7 @@ Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com> -} +{-# LANGUAGE CPP       #-} {-# LANGUAGE MagicHash #-}  module GHC.TypeLits.Extra.Solver.Operations@@ -26,6 +27,9 @@  -- external import Control.Monad.Trans.Writer.Strict+#if MIN_VERSION_ghc_typelits_natnormalise(0,7,0)+import Data.Set                     as Set+#endif  import GHC.Base                     (isTrue#,(==#),(+#)) import GHC.Integer                  (smallInteger)@@ -111,9 +115,15 @@   let x' = reifyEOP defs x       y' = reifyEOP defs y       z  = fst (runWriter (normaliseNat (mkTyConApp typeNatSubTyCon [y',x'])))+#if MIN_VERSION_ghc_typelits_natnormalise(0,7,0)+  in  case runWriterT (isNatural z) of+        Just (True , cs) | Set.null cs -> y+        Just (False, cs) | Set.null cs -> x+#else   in  case isNatural z of         Just True  -> y         Just False -> x+#endif         _ -> Max x y  mergeMin :: ExtraDefs -> ExtraOp -> ExtraOp -> ExtraOp@@ -121,9 +131,15 @@   let x' = reifyEOP defs x       y' = reifyEOP defs y       z  = fst (runWriter (normaliseNat (mkTyConApp typeNatSubTyCon [y',x'])))+#if MIN_VERSION_ghc_typelits_natnormalise(0,7,0)+  in  case runWriterT (isNatural z) of+        Just (True, cs) | Set.null cs -> x+        Just (False,cs) | Set.null cs -> y+#else   in  case isNatural z of         Just True  -> x         Just False -> y+#endif         _ -> Min x y  mergeDiv :: ExtraOp -> ExtraOp -> Maybe ExtraOp
src/GHC/TypeLits/Extra/Solver/Unify.hs view
@@ -18,8 +18,10 @@ -- external import Control.Monad.Trans.Class    (lift) import Control.Monad.Trans.Maybe    (MaybeT (..))+import Control.Monad.Trans.Writer.Strict (runWriter) import Data.Function                (on) import GHC.TypeLits.Normalise.Unify (CType (..))+import qualified GHC.TypeLits.Normalise.Unify as Normalise  -- GHC API import Outputable (Outputable (..), ($$), text)@@ -69,7 +71,8 @@   tyM    <- lift (matchFam tc tys')   case tyM of     Just (_,ty) -> normaliseNat defs ty-    _ -> return (C (CType (TyConApp tc tys)))+    _ -> let q = fst (runWriter (Normalise.normaliseNat (TyConApp tc tys)))+         in  return (C (CType (Normalise.reifySOP q)))  normaliseNat _ t = return (C (CType t)) 
tests/ErrorTests.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, DataKinds, TypeOperators, TypeApplications, TypeFamilies, TemplateHaskell #-}+{-# LANGUAGE CPP, DataKinds, TypeOperators, TypeApplications, TypeFamilies #-} #if __GLASGOW_HASKELL__ >= 805 {-# LANGUAGE NoStarIsType #-} #endif@@ -13,10 +13,6 @@ import GHC.TypeLits import GHC.TypeLits.Extra -import GHC.IO.Encoding            (getLocaleEncoding, textEncodingName, utf8)-import Language.Haskell.TH        (litE, stringL)-import Language.Haskell.TH.Syntax (runIO)- testFail1 :: Proxy (GCD 6 8) -> Proxy 4 testFail1 = id @@ -89,7 +85,18 @@ testFail23 :: () testFail23 = testFail23' (Proxy @18) (Proxy @3) +testFail24 :: Proxy x -> Proxy y -> Proxy z -> Proxy (z <=? Max x y) -> Proxy True+testFail24 _ _ _ = id +testFail25 :: Proxy x -> Proxy y -> Proxy (x+1 <=? Max x y) -> Proxy True+testFail25 _ _ = id++-- While n ~ (Max x y) implies x <= n (see test46), the reverse is not true.+testFail26' :: ((x <=? n) ~ True)  => Proxy x -> Proxy y -> Proxy n -> Proxy ((Max x y)) -> Proxy n+testFail26' _ _ _ = id++testFail26 = testFail26' (Proxy @4) (Proxy @6) (Proxy @6)+ testFail1Errors =   ["Expected type: Proxy (GCD 6 8) -> Proxy 4"   ,"Actual type: Proxy 4 -> Proxy 4"@@ -136,18 +143,10 @@   ]  testFail10Errors =-  [$(do localeEncoding <- runIO (getLocaleEncoding)-        if textEncodingName localeEncoding == textEncodingName utf8-          then litE $ stringL "Couldn't match type ‘'False’ with ‘'True’"-          else litE $ stringL "Couldn't match type 'False with 'True"-    )]+  ["Couldn't match type ‘'False’ with ‘'True’"]  testFail11Errors =-  [$(do localeEncoding <- runIO (getLocaleEncoding)-        if textEncodingName localeEncoding == textEncodingName utf8-          then litE $ stringL "Couldn't match type ‘CLog 2 4 <=? CLog 4 4’ with ‘'True’"-          else litE $ stringL "Couldn't match type `CLog 2 4 <=? CLog 4 4' with 'True"-    )]+  ["Couldn't match type ‘CLog 2 4 <=? CLog 4 4’ with ‘'True’"]  testFail12Errors =   ["Expected type: Proxy (Div 4 0) -> Proxy 4"@@ -185,18 +184,10 @@   ]  testFail19Errors =-  [$(do localeEncoding <- runIO (getLocaleEncoding)-        if textEncodingName localeEncoding == textEncodingName utf8-          then litE $ stringL "Couldn't match type ‘FLog 3 0’ with ‘CLog 3 0’"-          else litE $ stringL "Couldn't match type `FLog 3 0' with `CLog 3 0'"-    )]+  ["Couldn't match type ‘FLog 3 0’ with ‘CLog 3 0’"]  testFail20Errors =-  [$(do localeEncoding <- runIO (getLocaleEncoding)-        if textEncodingName localeEncoding == textEncodingName utf8-          then litE $ stringL "Couldn't match type ‘FLog 3 10’ with ‘CLog 3 10’"-          else litE $ stringL "Couldn't match type `FLog 3 10' with `CLog 3 10'"-    )]+  ["Couldn't match type ‘FLog 3 10’ with ‘CLog 3 10’"]  testFail21Errors =   ["Expected type: Proxy (Min a (a * b)) -> Proxy a"@@ -213,3 +204,14 @@ #else   ["Couldn't match type ‘1 <=? Div 18 3’ with ‘'False’"] #endif++testFail24Errors =+  ["Couldn't match type ‘z <=? Max x y’ with ‘'True’"]++testFail25Errors =+  ["Couldn't match type ‘(x + 1) <=? Max x y’ with ‘'True’"]++testFail26Errors =+  ["Could not deduce: Max x y ~ n"+  ,"from the context: (x <=? n) ~ 'True"+  ]
tests/Main.hs view
@@ -147,6 +147,42 @@ test43 :: Proxy x -> Proxy y -> Proxy (LCM x y) -> Proxy (LCM y x) test43 _ _ = id +test44 :: Proxy x -> Proxy y -> Proxy (x <=? (Max x y)) -> Proxy True+test44 _ _ = id++test45 :: Proxy x -> Proxy y -> Proxy (y <=? (Max x y)) -> Proxy True+test45 _ _ = id++test46 :: n ~ (Max x y) => Proxy x -> Proxy y -> Proxy (x <=? n) -> Proxy True+test46 _ _ = id++test47 :: n ~ (Max x y) => Proxy x -> Proxy y -> Proxy (y <=? n) -> Proxy True+test47 _ _ = id++test48+  :: Proxy n+  -> Proxy (Max (1+n) 1)+  -> Proxy (n+1)+test48 _ = id++test49+  :: Proxy n+  -> Proxy (Max (n+1) 1)+  -> Proxy (1+n)+test49 _ = id++test50+  :: Proxy n+  -> Proxy (Max (n+2) 1)+  -> Proxy (Max (2+n) 2)+test50 _ = id++test51+  :: Proxy n+  -> Proxy (Max (((2 ^ n) + 1) + ((2 ^ n) + 1)) 1)+  -> Proxy (2+((2^n)*2))+test51 _ = id+ main :: IO () main = defaultMain tests @@ -282,6 +318,30 @@     , testCase "forall x y . LCM x y ~ LCM y x" $       show (test43 Proxy Proxy Proxy) @?=       "Proxy"+    , testCase "forall x y . x <=? Max x y ~ True" $+      show (test44 Proxy Proxy Proxy) @?=+      "Proxy"+    , testCase "forall x y . y <=? Max x y ~ True" $+      show (test45 Proxy Proxy Proxy) @?=+      "Proxy"+    , testCase "forall x y n . n ~ Max x y => x <=? n ~ True" $+      show (test46 Proxy Proxy Proxy) @?=+      "Proxy"+    , testCase "forall x y n . n ~ Max x y => y <=? n ~ True" $+      show (test47 Proxy Proxy Proxy) @?=+      "Proxy"+    , testCase "forall n . Max (n+1) 1 ~ 1+n" $+      show (test48 Proxy Proxy) @?=+      "Proxy"+    , testCase "forall n . Max (1+n) 1 ~ n+1" $+      show (test49 Proxy Proxy) @?=+      "Proxy"+    , testCase "forall n . Max (n+2) 1 ~ Max (2+n) 2" $+      show (test50 Proxy Proxy) @?=+      "Proxy"+    , testCase "forall n . Max (((2 ^ n) + 1) + ((2 ^ n) + 1)) 1 ~ 2 + ((2 ^ n) * 2)" $+      show (test51 Proxy Proxy) @?=+      "Proxy"     ]   , testGroup "errors"     [ testCase "GCD 6 8 /~ 4" $ testFail1 `throws` testFail1Errors@@ -307,6 +367,9 @@     , testCase "Min a (a*b) /~ a" $ testFail21 `throws` testFail21Errors     , testCase "Max a (a*b) /~ (a*b)" $ testFail22 `throws` testFail22Errors     , testCase "(1 <=? Div 18 6) ~ False" $ testFail23 `throws` testFail23Errors+    , testCase "(z <=? Max x y) /~ True" $ testFail24 `throws` testFail24Errors+    , testCase "(x+1 <=? Max x y) /~ True" $ testFail25 `throws` testFail25Errors+    , testCase "(x <= n) /=> (Max x y) ~ n" $ testFail26 `throws` testFail26Errors     ]   ] @@ -319,6 +382,12 @@   case result of     Right _ -> assertFailure "No exception!"     Left (TypeError msg) ->-      if all (`isInfixOf` msg) xs+      if all (`isInfixOf` (removeProblemChars msg)) $ map removeProblemChars xs          then return ()          else assertFailure msg++-- The kind and amount of quotes in GHC error messages changes depending on+-- whether or not our locale supports unicode.+-- Remove the problematic characters to enable comparison of errors.+removeProblemChars = filter (`notElem` problemChars)+  where problemChars = "‘’`'"