packages feed

constraints-extras 0.3.2.1 → 0.4.0.2

raw patch · 6 files changed

Files

ChangeLog.md view
@@ -1,5 +1,32 @@ # Revision history for constraints-extras +## 0.4.0.2++* Support GHC 9.12++## 0.4.0.1++* Support GHC 9.10+* Bump `base` and `template-haskell` upper bounds++## 0.4.0.0 revision 2 - 2023-11-20++* Support GHC 9.8++  Bump `base` and `template-haskell` upper bounds++## 0.4.0.0 revision 1 - 2023-05-10++* Support GHC 9.6++  Bump `base` and `template-haskell` upper bounds++## 0.4.0.0 - 2022-11-18++* Make `Has` from a type synonym into the class upon which everything else is based. Instances may define either `has` or `argDict` which are now both methods of `Has`. This should hopefully improve the readability of type errors involving the library a fair bit, as everything wanted to use `Has`, but it was defined in terms of the less commonly appearing `ArgDict` and `ConstraintsFor`.+* The `ConstraintsFor` type family has been removed as it is now unnecessary, as instances of `Has` can simply be constrained directly. This has the added benefit of allowing `QuantifiedConstraints` in those instance heads that formerly would not have been allowed as part of the result of a type family.+* The `ArgDict` class has also been removed, as it was also basically never used on its own.+ ## 0.3.2.1 - 2021-12-17  * Support GHC 9.2
README.lhs view
@@ -1,4 +1,4 @@-constraints-extras [![travis-ci](https://api.travis-ci.org/obsidiansystems/constraints-extras.svg?branch=develop)](https://travis-ci.org/obsidiansystems/constraints-extras)+constraints-extras ==================  Example usage:@@ -14,10 +14,12 @@ > {-# LANGUAGE TypeApplications  #-} > {-# LANGUAGE TypeFamilies #-} > {-# LANGUAGE FlexibleContexts #-}+> {-# LANGUAGE ConstraintKinds #-} > {-# LANGUAGE FlexibleInstances #-} > {-# LANGUAGE MultiParamTypeClasses #-} > {-# LANGUAGE UndecidableInstances #-} > {-# LANGUAGE ExistentialQuantification #-}+> {-# LANGUAGE TypeFamilies #-} > > import Data.Aeson > import Data.Constraint.Forall@@ -41,6 +43,14 @@ > > deriveArgDict ''V >+> data family Fam a :: * -> *+> data instance Fam () :: * -> * where+>   FI :: Fam () Int+>   FB :: Fam () Bool+>+> deriveArgDict 'FI+> -- this derives an instance Has c (Fam ()) by looking up the associated data instance.+> > data DSum k f = forall a. DSum (k a) (f a) > > -- Derive a ToJSON instance for our 'DSum'@@ -65,4 +75,5 @@ > > main :: IO () > main = return ()+ ```
README.md view
@@ -1,4 +1,4 @@-constraints-extras [![travis-ci](https://api.travis-ci.org/obsidiansystems/constraints-extras.svg?branch=develop)](https://travis-ci.org/obsidiansystems/constraints-extras)+constraints-extras ==================  Example usage:@@ -14,10 +14,12 @@ > {-# LANGUAGE TypeApplications  #-} > {-# LANGUAGE TypeFamilies #-} > {-# LANGUAGE FlexibleContexts #-}+> {-# LANGUAGE ConstraintKinds #-} > {-# LANGUAGE FlexibleInstances #-} > {-# LANGUAGE MultiParamTypeClasses #-} > {-# LANGUAGE UndecidableInstances #-} > {-# LANGUAGE ExistentialQuantification #-}+> {-# LANGUAGE TypeFamilies #-} > > import Data.Aeson > import Data.Constraint.Forall@@ -41,6 +43,14 @@ > > deriveArgDict ''V >+> data family Fam a :: * -> *+> data instance Fam () :: * -> * where+>   FI :: Fam () Int+>   FB :: Fam () Bool+>+> deriveArgDict 'FI+> -- this derives an instance Has c (Fam ()) by looking up the associated data instance.+> > data DSum k f = forall a. DSum (k a) (f a) > > -- Derive a ToJSON instance for our 'DSum'@@ -65,4 +75,5 @@ > > main :: IO () > main = return ()+ ```
constraints-extras.cabal view
@@ -1,5 +1,5 @@ name: constraints-extras-version: 0.3.2.1+version: 0.4.0.2 synopsis: Utility package for constraints description: Convenience functions and TH for working with constraints. See <https://github.com/obsidiansystems/constraints-extras/blob/develop/README.md README.md> for example usage. category: Constraints@@ -13,9 +13,10 @@ build-type: Simple cabal-version: 2.0 tested-with:-  GHC  ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.1 || ==9.0.1 || ==9.2.1+  GHC ==8.6.5 || ==8.8.4 || ==8.10.7+   || ==9.0.2 || ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.4 || ==9.10.1 || ==9.12.1+extra-doc-files: ChangeLog.md extra-source-files: README.md-                    ChangeLog.md  flag build-readme   default: True@@ -32,11 +33,18 @@                   , TypeOperators                   , ConstraintKinds                   , TemplateHaskell-  build-depends: base >=4.9 && <4.17-               , constraints >= 0.9 && < 0.14-               , template-haskell >=2.11 && <2.19+  build-depends: base >=4.9 && <4.23+               , constraints >= 0.9 && < 0.15+               , template-haskell >=2.11 && <2.24+   hs-source-dirs:  src   default-language: Haskell2010++  -- This is needed to get around a bug/misfeature in the cabal solver which is choosing an+  -- old version of aeson (0.9.*) for some reason.+  if impl (ghc >= 9.12)+    build-depends: aeson >= 2+  executable readme   if !flag(build-readme)
src/Data/Constraint/Extras.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-}@@ -28,15 +29,11 @@ -- must come from the set { @Int@, @Bool@ }. We call this the "set of -- types @a@ that could be applied to @Tag@". module Data.Constraint.Extras-  ( -- * The ArgDict typeclass-    ArgDict(..)-  , ConstraintsFor'+  ( -- * The Has typeclass+    Has(..)   , argDict'-  , ConstraintsForV   , argDictV     -- * Bringing instances into scope-  , Has-  , has   , Has'   , has'   , HasV@@ -44,8 +41,6 @@   , whichever     -- * Misc   , Implies1(..)-    -- * Deprecated-  , ArgDictV   ) where  import Data.Constraint@@ -56,93 +51,69 @@ import Data.Kind import GHC.Generics ((:+:)(..)) --- | Morally, this class is for GADTs whose indices can be finitely--- enumerated. An @'ArgDict' c f@ instance allows us to do two things:------ 1. 'ConstraintsFor' requests the set of constraints @c a@ for all---    possible types @a@ that could be applied to @f@.------ 2. 'argDict' selects a specific @c a@ given a value of type @f a@.------ Use 'Data.Constraint.Extras.TH.deriveArgDict' to derive instances--- of this class.-class ArgDict (c :: k -> Constraint) (f :: k -> Type) where-  -- | Apply @c@ to each possible type @a@ that could appear in a @f a@.+-- | The constraint @Has c f@ means that given any value of type @f a@, we can determine+-- that there is an instance of @c a@. For example, @Has Show Tag@ means that given any+-- @x :: Tag a@, we can conclude @Show a@. Most commonly, the type @f@ will be a GADT,+-- where we can enumerate all the possible index types through pattern matching, and+-- discover that there is an appropriate instance in each case. In this sort of+-- situation, the @c@ can be left entirely polymorphic in the instance for @Has@, and+-- this is the sort of instance that the provided Template Haskell code writes.++-- It is also sometimes possible to hand-write instances of @Has c f@ for specific+-- classes @c@ in cases where @f@ is a data type that packs an appropriate dictionary+-- into its constructors.+class Has c f where+  -- | Use the @f a@ to show that there is an instance of @c a@, and+  -- bring it into scope.   ---  -- > ConstraintsFor Show Tag = (Show Int, Show Bool)-  type ConstraintsFor f c :: Constraint+  -- The order of type variables is chosen to work+  -- with @-XTypeApplications@.+  --+  -- > -- Hold a value of type a, along with a tag identifying the a.+  -- > data SomeTagged tag where+  -- >   SomeTagged :: a -> tag a -> SomeTagged tag+  -- >+  -- > -- Use the stored tag to identify the thing we have, allowing us to call 'show'. Note that we+  -- > -- have no knowledge of the tag type.+  -- > showSomeTagged :: Has Show tag => SomeTagged tag -> String+  -- > showSomeTagged (SomeTagged a tag) = has @Show tag $ show a+  has :: forall a r. f a -> (c a => r) -> r+  has x r | Dict <- argDict @c x = r -  -- | Use an @f a@ to select a specific dictionary from @ConstraintsFor f c@.+  -- | Use an @f a@ to obtain a dictionary for @c a@   ---  -- > argDict I :: Dict (Show Int)-  argDict :: ConstraintsFor f c => f a -> Dict (c a)+  -- > argDict @Show I :: Dict (Show Int)+  argDict :: forall a. f a -> Dict (c a)+  argDict x = has @c x Dict+  {-# MINIMAL has | argDict #-}  -- | @since 0.3.2.0-instance (ArgDict c f, ArgDict c g) => ArgDict c (f :+: g) where-  type ConstraintsFor (f :+: g) c = (ConstraintsFor f c, ConstraintsFor g c)+instance (Has c f, Has c g) => Has c (f :+: g) where   argDict = \case     L1 f -> argDict f     R1 g -> argDict g  -- | @since 0.3.2.0-instance (ArgDict c f, ArgDict c g) => ArgDict c (Sum f g) where-  type ConstraintsFor (Sum f g) c = (ConstraintsFor f c, ConstraintsFor g c)+instance (Has c f, Has c g) => Has c (Sum f g) where   argDict = \case     InL f -> argDict f     InR g -> argDict g --- | \"Primed\" variants (@ConstraintsFor'@, 'argDict'', 'Has'',--- 'has'', &c.) use the 'ArgDict' instance on @f@ to apply constraints--- on @g a@ instead of just @a@. This is often useful when you have--- data structures parameterised by something of kind @(x -> Type) ->--- Type@, like in the @dependent-sum@ and @dependent-map@ libraries.------ > ConstraintsFor' Tag Show Identity = (Show (Identity Int), Show (Identity Bool))-type ConstraintsFor' f (c :: k -> Constraint) (g :: k' -> k) = ConstraintsFor f (ComposeC c g)---- | Get a dictionary for a specific @g a@, using a value of type @f a@.------ > argDict' B :: Dict (Show (Identity Bool))-argDict' :: forall f c g a. (Has' c f g) => f a -> Dict (c (g a))-argDict' tag = case argDict tag of-  (Dict :: Dict (ComposeC c g a)) -> Dict--type ConstraintsForV (f :: (k -> k') -> Type) (c :: k' -> Constraint) (g :: k) = ConstraintsFor f (FlipC (ComposeC c) g)--argDictV :: forall f c g v. (HasV c f g) => f v -> Dict (c (v g))-argDictV tag = case argDict tag of-  (Dict :: Dict (FlipC (ComposeC c) g a)) -> Dict--{-# DEPRECATED ArgDictV "Just use 'ArgDict'" #-}-type ArgDictV f c = ArgDict f c+-- | The constraint @Has' c f g@ means that given a value of type @f a@, we can satisfy the constraint @c (g a)@.+type Has' (c :: k -> Constraint) f (g :: k' -> k) = Has (ComposeC c g) f --- | @Has c f@ is a constraint which means that for every type @a@--- that could be applied to @f@, we have @c a@.------ > Has Show Tag = (ArgDict Show Tag, Show Int, Show Bool)-type Has (c :: k -> Constraint) f = (ArgDict c f, ConstraintsFor f c)+-- | The constraint @HasV c f g@ means that given a value of type @f v@, we can satisfy the constraint @c (v g)@.+type HasV c f g = Has (FlipC (ComposeC c) g) f --- | @Has' c f g@ is a constraint which means that for every type @a@--- that could be applied to @f@, we have @c (g a)@.+-- | Get a dictionary for @c (g a)@, using a value of type @f a@. ----- > Has' Show Tag Identity = (ArgDict (Show . Identity) Tag, Show (Identity Int), Show (Identity Bool))-type Has' (c :: k -> Constraint) f (g :: k' -> k) = (ArgDict (ComposeC c g) f, ConstraintsFor' f c g)-type HasV c f g = (ArgDict (FlipC (ComposeC c) g) f, ConstraintsForV f c g)+-- > argDict' @Show @Identity B :: Dict (Show (Identity Bool))+argDict' :: forall c g f a. (Has' c f g) => f a -> Dict (c (g a))+argDict' x = has @(ComposeC c g) x Dict --- | Use the @a@ from @f a@ to select a specific @c a@ constraint, and--- bring it into scope. The order of type variables is chosen to work--- with @-XTypeApplications@.------ > -- Hold an a, along with a tag identifying the a.--- > data SomeTagged tag where--- >   SomeTagged :: a -> tag a -> SomeTagged tag--- >--- > -- Use the stored tag to identify the thing we have, allowing us to call 'show'. Note that we--- > -- have no knowledge of the tag type.--- > showSomeTagged :: Has Show tag => SomeTagged tag -> String--- > showSomeTagged (SomeTagged a tag) = has @Show tag $ show a-has :: forall c f a r. Has c f => f a -> (c a => r) -> r-has k r | (Dict :: Dict (c a)) <- argDict k = r+-- | Get a dictionary for @c (v g)@, using a value of type @f v@.+argDictV :: forall f c g v. (HasV c f g) => f v -> Dict (c (v g))+argDictV x = has @(FlipC (ComposeC c) g) x Dict  -- | Like 'has', but we get a @c (g a)@ instance brought into scope -- instead. Use @-XTypeApplications@ to specify @c@ and @g@.@@ -153,11 +124,12 @@ -- > -- Show the value from a dependent sum. (We'll need 'whichever', discussed later, to show the key.) -- > showDSumVal :: forall tag f . Has' Show tag f => DSum tag f -> String -- > showDSumVal (tag :=> fa) = has' @Show @f tag $ show fa-has' :: forall c g f a r. Has' c f g => f a -> (c (g a) => r) -> r-has' k r | (Dict :: Dict (c (g a))) <- argDict' k = r+has' :: forall c g f a r. (Has' c f g) => f a -> (c (g a) => r) -> r+has' k r = has @(ComposeC c g) k r -hasV :: forall c g f v r. HasV c f g => f v -> (c (v g) => r) -> r-hasV k r | (Dict :: Dict (c (v g))) <- argDictV k = r+-- | Similar to 'has', but given a value of type @f v@, we get a @c (v g)@ instance brought into scope instead.+hasV :: forall c g f v r. (HasV c f g) => f v -> (c (v g) => r) -> r+hasV k r = has @(FlipC (ComposeC c) g) k r  -- | Given "forall a. @c (t a)@" (the @ForallF c t@ constraint), select a -- specific @a@, and bring @c (t a)@ into scope. Use @-XTypeApplications@ to
src/Data/Constraint/Extras/TH.hs view
@@ -16,15 +16,14 @@   (typeHead, constrs) <- getDeclInfo n   c <- newName "c"   ts <- gadtIndices c constrs-  let xs = flip map ts $ \case-        Left t -> AppT (AppT (ConT ''ConstraintsFor) t) (VarT c)+  let constraints = flip map ts $ \case+        Left t -> AppT (AppT (ConT ''Has) (VarT c)) t         Right t -> (AppT (VarT c) t)-      l = length xs-      constraints = foldl AppT (TupleT l) xs-  [d| instance ArgDict $(varT c) $(pure typeHead) where-        type ConstraintsFor  $(pure typeHead) $(varT c) = $(pure constraints)-        argDict = $(LamCaseE <$> matches c constrs 'argDict)-    |]+  ms <- matches c constrs 'argDict+  return+    [ InstanceD Nothing constraints (AppT (AppT (ConT ''Has) (VarT c)) typeHead)+      [ ValD (VarP 'argDict) (NormalB (LamCaseE ms)) [] ]+    ]  {-# DEPRECATED deriveArgDictV "Just use 'deriveArgDict'" #-} deriveArgDictV :: Name -> Q [Dec]@@ -33,13 +32,13 @@ matches :: Name -> [Con] -> Name -> Q [Match] matches c constrs argDictName = do   x <- newName "x"-  fmap concat $ forM constrs $ \case+  fmap concat . forM constrs $ \case     GadtC [name] _ _ -> return $       [Match (RecP name []) (NormalB $ ConE 'Dict) []]     ForallC _ _ (GadtC [name] bts (AppT _ (VarT b))) -> do       ps <- forM bts $ \case         (_, AppT t (VarT b')) | b == b' -> do-          hasArgDictInstance <- not . null <$> reifyInstances ''ArgDict [VarT c, t]+          hasArgDictInstance <- not . null <$> reifyInstances ''Has [VarT c, t]           return $ if hasArgDictInstance             then Just x             else Nothing@@ -81,8 +80,11 @@     tyVars <- replicateM (arity - 1) (newName "a")     let typeHead = foldr (\v x -> AppT x (VarT v)) (ConT n) tyVars     return (typeHead, constrs)-  DataConI _ (ForallT _ _ (AppT typeHead _)) parent -> do-    reify parent >>= \case+  DataConI _ (AppT typeHead _) parent -> handleParent typeHead parent+  DataConI _ (ForallT _ _ (AppT typeHead _)) parent -> handleParent typeHead parent+  a -> error $ "getDeclInfo: Unmatched 'Info': " ++ show a+  where+    handleParent typeHead parent = reify parent >>= \case       FamilyI _ instances -> do         let instCons :: InstanceDec -> [Con]             instCons = \case@@ -103,14 +105,13 @@           l@(_:_:_) -> error $ "getDeclInfo: Expected one data family instance for constructor " ++ show n ++ " but found multiple: " ++ show l           [i] -> return (typeHead, instCons i)       a -> error $ "getDeclInfo: Unmatched parent of data family instance: " ++ show a-  a -> error $ "getDeclInfo: Unmatched 'Info': " ++ show a  gadtIndices :: Name -> [Con] -> Q [Either Type Type] gadtIndices c constrs = fmap concat $ forM constrs $ \case   GadtC _ _ (AppT _ typ) -> return [Right typ]   ForallC _ _ (GadtC _ bts (AppT _ (VarT _))) -> fmap concat $ forM bts $ \case     (_, AppT t (VarT _)) -> do-      hasArgDictInstance <- fmap (not . null) $ reifyInstances ''ArgDict [VarT c, t]+      hasArgDictInstance <- fmap (not . null) $ reifyInstances ''Has [VarT c, t]       return $ if hasArgDictInstance then [Left t] else []     _ -> return []   ForallC _ _ (GadtC _ _ (AppT _ typ)) -> return [Right typ]