invariant 0.1.2 → 0.2
raw patch · 8 files changed
+2187/−156 lines, 8 filesdep +arraydep +bifunctorsdep +containersPVP ok
version bump matches the API change (PVP)
Dependencies added: array, bifunctors, containers, ghc-prim, profunctors, semigroups, stm, tagged, template-haskell, transformers, transformers-compat, unordered-containers
API changes (from Hackage documentation)
+ Data.Functor.Invariant: WrapBifunctor :: p a b -> WrappedBifunctor a b
+ Data.Functor.Invariant: WrapContravariant :: f a -> WrappedContravariant f a
+ Data.Functor.Invariant: WrapFunctor :: f a -> WrappedFunctor f a
+ Data.Functor.Invariant: WrapProfunctor :: p a b -> WrappedProfunctor p a b
+ Data.Functor.Invariant: instance (Invariant f, Invariant g) => Invariant (Product f g)
+ Data.Functor.Invariant: instance (Invariant f, Invariant g) => Invariant (Sum f g)
+ Data.Functor.Invariant: instance (Invariant f, Invariant g) => Invariant (f :.: g)
+ Data.Functor.Invariant: instance (Invariant f, Invariant2 p) => Invariant (Tannen f p a)
+ Data.Functor.Invariant: instance (Invariant f, Invariant2 p) => Invariant2 (Cayley f p)
+ Data.Functor.Invariant: instance (Invariant f, Invariant2 p) => Invariant2 (Tannen f p)
+ Data.Functor.Invariant: instance (Invariant l, Invariant r) => Invariant (l :*: r)
+ Data.Functor.Invariant: instance (Invariant l, Invariant r) => Invariant (l :+: r)
+ Data.Functor.Invariant: instance (Invariant2 f, Invariant2 g) => Invariant2 (Product f g)
+ Data.Functor.Invariant: instance (Invariant2 p, Invariant f, Invariant g) => Invariant2 (Biff p f g)
+ Data.Functor.Invariant: instance (Invariant2 p, Invariant g) => Invariant (Biff p f g a)
+ Data.Functor.Invariant: instance (Invariant2 p, Invariant2 q) => Invariant2 (Procompose p q)
+ Data.Functor.Invariant: instance (Invariant2 p, Invariant2 q) => Invariant2 (Ran p q)
+ Data.Functor.Invariant: instance (Invariant2 p, Invariant2 q) => Invariant2 (Rift p q)
+ Data.Functor.Invariant: instance Alternative f => Alternative (WrappedFunctor f)
+ Data.Functor.Invariant: instance Applicative f => Applicative (WrappedFunctor f)
+ Data.Functor.Invariant: instance Arrow a => Invariant (ArrowMonad a)
+ Data.Functor.Invariant: instance Bifunctor p => Invariant (WrappedBifunctor p a)
+ Data.Functor.Invariant: instance Bifunctor p => Invariant2 (WrappedBifunctor p)
+ Data.Functor.Invariant: instance Choice p => Choice (WrappedProfunctor p)
+ Data.Functor.Invariant: instance Closed p => Closed (WrappedProfunctor p)
+ Data.Functor.Invariant: instance Cochoice p => Cochoice (WrappedProfunctor p)
+ Data.Functor.Invariant: instance Contravariant f => Contravariant (WrappedContravariant f)
+ Data.Functor.Invariant: instance Contravariant f => Invariant (WrappedContravariant f)
+ Data.Functor.Invariant: instance Costrong p => Costrong (WrappedProfunctor p)
+ Data.Functor.Invariant: instance Decidable f => Decidable (WrappedContravariant f)
+ Data.Functor.Invariant: instance Divisible f => Divisible (WrappedContravariant f)
+ Data.Functor.Invariant: instance Eq (f a) => Eq (WrappedContravariant f a)
+ Data.Functor.Invariant: instance Eq (f a) => Eq (WrappedFunctor f a)
+ Data.Functor.Invariant: instance Eq (p a b) => Eq (WrappedProfunctor p a b)
+ Data.Functor.Invariant: instance Functor f => Functor (WrappedFunctor f)
+ Data.Functor.Invariant: instance Functor f => Invariant (WrappedFunctor f)
+ Data.Functor.Invariant: instance Invariant (Arg a)
+ Data.Functor.Invariant: instance Invariant (Clown f a)
+ Data.Functor.Invariant: instance Invariant (Constant a)
+ Data.Functor.Invariant: instance Invariant (ContT r m)
+ Data.Functor.Invariant: instance Invariant (Costar f a)
+ Data.Functor.Invariant: instance Invariant (Forget r a)
+ Data.Functor.Invariant: instance Invariant (HashMap k)
+ Data.Functor.Invariant: instance Invariant (K1 i c)
+ Data.Functor.Invariant: instance Invariant (Map k)
+ Data.Functor.Invariant: instance Invariant (Tagged s)
+ Data.Functor.Invariant: instance Invariant ArgDescr
+ Data.Functor.Invariant: instance Invariant ArgOrder
+ Data.Functor.Invariant: instance Invariant First
+ Data.Functor.Invariant: instance Invariant Handler
+ Data.Functor.Invariant: instance Invariant Identity
+ Data.Functor.Invariant: instance Invariant IntMap
+ Data.Functor.Invariant: instance Invariant Last
+ Data.Functor.Invariant: instance Invariant Max
+ Data.Functor.Invariant: instance Invariant Min
+ Data.Functor.Invariant: instance Invariant NonEmpty
+ Data.Functor.Invariant: instance Invariant OptDescr
+ Data.Functor.Invariant: instance Invariant Option
+ Data.Functor.Invariant: instance Invariant Par1
+ Data.Functor.Invariant: instance Invariant Proxy
+ Data.Functor.Invariant: instance Invariant STM
+ Data.Functor.Invariant: instance Invariant Seq
+ Data.Functor.Invariant: instance Invariant Tree
+ Data.Functor.Invariant: instance Invariant U1
+ Data.Functor.Invariant: instance Invariant V1
+ Data.Functor.Invariant: instance Invariant ViewL
+ Data.Functor.Invariant: instance Invariant ViewR
+ Data.Functor.Invariant: instance Invariant f => Invariant (Backwards f)
+ Data.Functor.Invariant: instance Invariant f => Invariant (Lift f)
+ Data.Functor.Invariant: instance Invariant f => Invariant (M1 i t f)
+ Data.Functor.Invariant: instance Invariant f => Invariant (Rec1 f)
+ Data.Functor.Invariant: instance Invariant f => Invariant (Reverse f)
+ Data.Functor.Invariant: instance Invariant f => Invariant (Star f a)
+ Data.Functor.Invariant: instance Invariant f => Invariant2 (Clown f)
+ Data.Functor.Invariant: instance Invariant f => Invariant2 (Costar f)
+ Data.Functor.Invariant: instance Invariant f => Invariant2 (Star f)
+ Data.Functor.Invariant: instance Invariant g => Invariant (Joker g a)
+ Data.Functor.Invariant: instance Invariant g => Invariant2 (Joker g)
+ Data.Functor.Invariant: instance Invariant m => Invariant (ErrorT e m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (ExceptT e m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (IdentityT m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (ListT m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (MaybeT m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (RWST r w s m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (ReaderT r m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (StateT s m)
+ Data.Functor.Invariant: instance Invariant m => Invariant (WriterT w m)
+ Data.Functor.Invariant: instance Invariant2 (Copastro p)
+ Data.Functor.Invariant: instance Invariant2 (Environment p)
+ Data.Functor.Invariant: instance Invariant2 (Forget r)
+ Data.Functor.Invariant: instance Invariant2 (K1 i)
+ Data.Functor.Invariant: instance Invariant2 (Pastro p)
+ Data.Functor.Invariant: instance Invariant2 Arg
+ Data.Functor.Invariant: instance Invariant2 Constant
+ Data.Functor.Invariant: instance Invariant2 Tagged
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Closure p a)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Codensity p a)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Cotambara p a)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Flip p a)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Join p)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Procompose p q a)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Rift p q a)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant (Tambara p a)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant2 (Closure p)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant2 (Codensity p)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant2 (Cotambara p)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant2 (Flip p)
+ Data.Functor.Invariant: instance Invariant2 p => Invariant2 (Tambara p)
+ Data.Functor.Invariant: instance Invariant2 q => Invariant (Ran p q a)
+ Data.Functor.Invariant: instance Ix i => Invariant (Array i)
+ Data.Functor.Invariant: instance Monad m => Monad (WrappedFunctor m)
+ Data.Functor.Invariant: instance MonadPlus m => MonadPlus (WrappedFunctor m)
+ Data.Functor.Invariant: instance Ord (f a) => Ord (WrappedContravariant f a)
+ Data.Functor.Invariant: instance Ord (f a) => Ord (WrappedFunctor f a)
+ Data.Functor.Invariant: instance Ord (p a b) => Ord (WrappedProfunctor p a b)
+ Data.Functor.Invariant: instance Profunctor p => Invariant (WrappedProfunctor p a)
+ Data.Functor.Invariant: instance Profunctor p => Invariant2 (WrappedProfunctor p)
+ Data.Functor.Invariant: instance Profunctor p => Profunctor (WrappedProfunctor p)
+ Data.Functor.Invariant: instance Read (f a) => Read (WrappedContravariant f a)
+ Data.Functor.Invariant: instance Read (f a) => Read (WrappedFunctor f a)
+ Data.Functor.Invariant: instance Read (p a b) => Read (WrappedProfunctor p a b)
+ Data.Functor.Invariant: instance Show (f a) => Show (WrappedContravariant f a)
+ Data.Functor.Invariant: instance Show (f a) => Show (WrappedFunctor f a)
+ Data.Functor.Invariant: instance Show (p a b) => Show (WrappedProfunctor p a b)
+ Data.Functor.Invariant: instance Strong p => Strong (WrappedProfunctor p)
+ Data.Functor.Invariant: invmap2Bifunctor :: Bifunctor f => (a -> c) -> (c -> a) -> (b -> d) -> (d -> b) -> f a b -> f c d
+ Data.Functor.Invariant: invmap2Profunctor :: Profunctor f => (a -> c) -> (c -> a) -> (b -> d) -> (d -> b) -> f a b -> f c d
+ Data.Functor.Invariant: invmapContravariant :: Contravariant f => (a -> b) -> (b -> a) -> f a -> f b
+ Data.Functor.Invariant: invmapFunctor :: Functor f => (a -> b) -> (b -> a) -> f a -> f b
+ Data.Functor.Invariant: newtype WrappedBifunctor (p :: * -> * -> *) a b :: (* -> * -> *) -> * -> * -> *
+ Data.Functor.Invariant: newtype WrappedContravariant f a
+ Data.Functor.Invariant: newtype WrappedFunctor f a
+ Data.Functor.Invariant: newtype WrappedProfunctor p a b
+ Data.Functor.Invariant: unwrapBifunctor :: WrappedBifunctor a b -> p a b
+ Data.Functor.Invariant: unwrapContravariant :: WrappedContravariant f a -> f a
+ Data.Functor.Invariant: unwrapFunctor :: WrappedFunctor f a -> f a
+ Data.Functor.Invariant: unwrapProfunctor :: WrappedProfunctor p a b -> p a b
+ Data.Functor.Invariant.TH: deriveInvariant :: Name -> Q [Dec]
+ Data.Functor.Invariant.TH: deriveInvariant2 :: Name -> Q [Dec]
+ Data.Functor.Invariant.TH: makeInvmap :: Name -> Q Exp
+ Data.Functor.Invariant.TH: makeInvmap2 :: Name -> Q Exp
Files
- CHANGELOG.md +15/−0
- Data/Functor/Invariant.hs +0/−125
- invariant.cabal +47/−31
- src/Data/Functor/Invariant.hs +738/−0
- src/Data/Functor/Invariant/TH.hs +785/−0
- src/Data/Functor/Invariant/TH/Internal.hs +437/−0
- test/InvariantSpec.hs +60/−0
- test/THSpec.hs +105/−0
CHANGELOG.md view
@@ -1,3 +1,18 @@+# 0.2+* Support deriving `Invariant` and `Invariant2` instances with Template Haskell+* Added `invmapFunctor`, `invmapContravariant`, `invmap2Bifunctor`, and+ `invmap2Profunctor` to make defining `Invmap` and `Invmap2` instances+ somewhat easier+* Added `WrappedFunctor`, `WrappedContravariant`, `WrappedBifunctor`, and+ `WrappedProfunctor` data types to allow use of `invmap` and `invmap2` for+ data types that aren't `Invariant` or `Invariant2` instances.+* Added `Invariant` instances for lazy `ST`, `ArrowMonad`, `Handler`,+ `Identity`, `First`, `Last`, `Alt`, `Proxy`, `ArgDescr`, `ArgOrder`, and+ `OptDescr`+* Added `Invariant` and `Invariant2` instances for data types in the `array`,+ `bifunctors`, `containers`, `profunctors`, `semigroups`, `stm`, `tagged`,+ `transformers`, and `unordered-containers` libraries+ # 0.1.2 * Add `Invariant` instances for `Dual` and `Endo`
− Data/Functor/Invariant.hs
@@ -1,125 +0,0 @@-module Data.Functor.Invariant (Invariant(..), Invariant2(..)) where--import Text.ParserCombinators.ReadP (ReadP)-import Text.ParserCombinators.ReadPrec (ReadPrec)--import qualified Control.Category as Cat-import Control.Arrow (Arrow(..))-import Control.Applicative (Const(Const), ZipList)-import Control.Applicative (WrappedMonad, WrappedArrow(WrapArrow))--import Control.Monad.ST (ST)--import Data.Functor.Contravariant-import Data.Functor.Contravariant.Compose-import Data.Monoid (Dual(Dual), Endo(Endo))------- | Any @*->*@ type parametric in the argument permits an instance of--- @Invariant@.------ Instances should satisfy the following laws:------ > invmap id id = id--- > invmap f2 f2' . invmap f1 f1' = invmap (f2 . f1) (f1' . f2')-class Invariant f where- invmap :: (a -> b) -> (b -> a) -> f a -> f b-------- | Any @*->*->*@ type parametric in both arguments permits an instance of--- @Invariant2@.------ Instances should satisfy the following laws:------ > invmap2 id id id id = id--- > invmap2 f2 f2' g2 g2' . invmap2 f1 f1' g1 g1' =--- > invmap2 (f2 . f1) (f1' . f2') (g2 . g1) (g1' . g2')-class Invariant2 f where- invmap2 :: (a -> c) -> (c -> a) -> (b -> d) -> (d -> b) -> f a b -> f c d------instance Invariant Maybe where invmap = flip $ const fmap-instance Invariant [] where invmap = flip $ const fmap-instance Invariant IO where invmap = flip $ const fmap-instance Invariant (ST s) where invmap = flip $ const fmap-instance Invariant ReadP where invmap = flip $ const fmap-instance Invariant ReadPrec where invmap = flip $ const fmap-instance Invariant ((->) a) where invmap = flip $ const fmap-instance Invariant (Either a) where invmap = flip $ const fmap-instance Invariant ((,) a) where invmap = flip $ const fmap-instance Invariant ((,,) a b) where invmap f _ ~(a, b, x) = (a, b, f x)-instance Invariant ((,,,) a b c) where- invmap f _ ~(a, b, c, x) = (a, b, c, f x)-instance Invariant ((,,,,) a b c d) where- invmap f _ ~(a, b, c, d, x) = (a, b, c, d, f x)--instance Invariant2 (->) where invmap2 _ f' g _ = (g .) . (. f')-instance Invariant2 Either where- invmap2 f _ _ _ (Left x) = Left $ f x- invmap2 _ _ g _ (Right y) = Right $ g y-instance Invariant2 (,) where invmap2 f _ g _ ~(x, y) = (f x, g y)-instance Invariant2 ((,,) a) where invmap2 f _ g _ ~(a, x, y) = (a, f x, g y)-instance Invariant2 ((,,,) a b) where- invmap2 f _ g _ ~(a, b, x, y) = (a, b, f x, g y)-instance Invariant2 ((,,,,) a b c) where- invmap2 f _ g _ ~(a, b, c, x, y) = (a, b, c, f x, g y)-------- | @Control.Applicative@-instance Invariant (Const a) where invmap _ _ (Const x) = Const x--- | @Control.Applicative@-instance Invariant ZipList where invmap = flip $ const fmap--- | @Control.Applicative@-instance Monad m => Invariant (WrappedMonad m) where invmap = flip $ const fmap--- | @Control.Applicative@-instance Arrow arr => Invariant (WrappedArrow arr a) where- invmap f _ (WrapArrow x) = WrapArrow $ ((arr f) Cat.. x)--- | @Control.Applicative@-instance Invariant2 Const where invmap2 f _ _ _ (Const x) = Const (f x)--- | @Control.Applicative@-instance Arrow arr => Invariant2 (WrappedArrow arr) where- invmap2 _ f' g _ (WrapArrow x) = WrapArrow $ arr g Cat.. x Cat.. arr f'---- | @Data.Monoid@-instance Invariant Dual where invmap f _ (Dual x) = Dual (f x)---- | @Data.Monoid@-instance Invariant Endo where- invmap f g (Endo x) = Endo (f . x . g)---- | from the @contravariant@ package-instance Invariant Predicate where invmap = const contramap--- | from the @contravariant@ package-instance Invariant Comparison where invmap = const contramap--- | from the @contravariant@ package-instance Invariant Equivalence where invmap = const contramap--- | from the @contravariant@ package-instance Invariant (Op a) where invmap = const contramap--- | from the @contravariant@ package-instance Invariant2 Op where- invmap2 f f' g g' (Op x) = Op $ invmap2 g g' f f' x-------- | from the @contravariant@ package-instance (Invariant f, Invariant g) => Invariant (Compose f g) where- invmap f g (Compose x) = Compose $ invmap (invmap f g) (invmap g f) x--- | from the @contravariant@ package-instance (Invariant f, Invariant g) => Invariant (ComposeCF f g) where- invmap f g (ComposeCF x) = ComposeCF $ invmap (invmap f g) (invmap g f) x--- | from the @contravariant@ package-instance (Invariant f, Invariant g) => Invariant (ComposeFC f g) where- invmap f g (ComposeFC x) = ComposeFC $ invmap (invmap f g) (invmap g f) x
invariant.cabal view
@@ -1,37 +1,53 @@-name: invariant-version: 0.1.2-synopsis: Haskell 98 invariant functors-description: Haskell 98 invariant functors--category: Control, Data--license: BSD3-license-file: LICENSE-author: Nicolas Frisby <nicolas.frisby@gmail.com>-maintainer: Nicolas Frisby <nicolas.frisby@gmail.com>, Ryan Scott <ryan.gl.scott@ku.edu>--build-type: Simple-cabal-version: >= 1.9.2-extra-source-files: CHANGELOG.md, README.md+name: invariant+version: 0.2+synopsis: Haskell 98 invariant functors+description: Haskell 98 invariant functors+category: Control, Data+license: BSD3+license-file: LICENSE+homepage: https://github.com/nfrisby/invariant-functors+bug-reports: https://github.com/nfrisby/invariant-functors/issues+author: Nicolas Frisby <nicolas.frisby@gmail.com>+maintainer: Nicolas Frisby <nicolas.frisby@gmail.com>,+ Ryan Scott <ryan.gl.scott@ku.edu>+build-type: Simple+cabal-version: >= 1.9.2+extra-source-files: CHANGELOG.md, README.md source-repository head- type: git- location: git://github.com/nfrisby/invariant-functors.git+ type: git+ location: https://github.com/nfrisby/invariant-functors library- build-depends:- base >= 4 && < 5- , contravariant >= 0.1.2 && < 2- exposed-modules: Data.Functor.Invariant- ghc-options: -Wall+ exposed-modules: Data.Functor.Invariant+ , Data.Functor.Invariant.TH+ other-modules: Data.Functor.Invariant.TH.Internal+ , Paths_invariant+ hs-source-dirs: src+ build-depends: array >= 0.3 && < 0.6+ , base >= 4 && < 5+ , bifunctors >= 5 && < 6+ , containers >= 0.1 && < 0.6+ , contravariant >= 0.1.2 && < 2+ , ghc-prim+ , profunctors >= 5 && < 6+ , semigroups >= 0.16.2 && < 1+ , stm >= 2.2 && < 3+ , tagged >= 0.7.3 && < 1+ , template-haskell >= 2.4 && < 2.11+ , transformers >= 0.2 && < 0.5+ , transformers-compat >= 0.3 && < 1+ , unordered-containers >= 0.2.4 && < 0.3+ ghc-options: -Wall test-suite qc-tests- type: exitcode-stdio-1.0- hs-source-dirs: test- main-is: Spec.hs- build-depends:- base >= 4 && < 5- , hspec >= 1.8- , invariant- , QuickCheck >= 2 && < 3- ghc-options: -Wall+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ other-modules: InvariantSpec+ THSpec+ build-depends: base >= 4 && < 5+ , hspec >= 1.8+ , invariant+ , QuickCheck >= 2 && < 3+ ghc-options: -Wall
+ src/Data/Functor/Invariant.hs view
@@ -0,0 +1,738 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-deprecations #-}++#define GHC_GENERICS_OK __GLASGOW_HASKELL__ >= 702++{-|+Module: Data.Functor.Invariant+Copyright: (C) 2012-2015 Nicolas Frisby, (C) 2015 Ryan Scott+License: BSD-style (see the file LICENSE)+Maintainer: Ryan Scott+Portability: Portable++Haskell98 invariant functors (also known as exponential functors).++For more information, see Edward Kmett's article \"Rotten Bananas\":++<http://comonad.com/reader/2008/rotten-bananas/>++-}+module Data.Functor.Invariant+ ( -- * @Invariant@+ Invariant(..)+ , invmapFunctor+ , WrappedFunctor(..)+ , invmapContravariant+ , WrappedContravariant(..)+#if GHC_GENERICS_OK+ -- ** @GHC.Generics@+ -- $ghcgenerics+#endif+ -- * @Invariant2@+ , Invariant2(..)+ , invmap2Bifunctor+ , WrappedBifunctor(..)+ , invmap2Profunctor+ , WrappedProfunctor(..)+ ) where++-- base+import qualified Control.Category as Cat+import Control.Arrow+import Control.Applicative as App+import Control.Exception (Handler(..))+import Control.Monad (MonadPlus(..))+import qualified Control.Monad.ST as Strict (ST)+import qualified Control.Monad.ST.Lazy as Lazy (ST)+import Data.Functor.Identity (Identity)+#if __GLASGOW_HASKELL__ < 711+import Data.Ix (Ix)+#endif+import qualified Data.Monoid as Monoid (First(..), Last(..))+#if MIN_VERSION_base(4,8,0)+import Data.Monoid (Alt(..))+#endif+import Data.Monoid (Dual(..), Endo(..))+import Data.Proxy (Proxy(..))+#if GHC_GENERICS_OK+import GHC.Generics+#endif+import System.Console.GetOpt as GetOpt+import Text.ParserCombinators.ReadP (ReadP)+import Text.ParserCombinators.ReadPrec (ReadPrec)++-- array+import Data.Array (Array)++-- bifunctors+import Data.Bifunctor hiding (first)+import Data.Bifunctor.Biff+import Data.Bifunctor.Clown+import Data.Bifunctor.Flip+import Data.Bifunctor.Join+import Data.Bifunctor.Joker+import qualified Data.Bifunctor.Product as Bifunctors+import Data.Bifunctor.Tannen+import Data.Bifunctor.Wrapped++-- containers+import Data.IntMap (IntMap)+import Data.Map (Map)+import Data.Sequence (Seq, ViewL, ViewR)+import Data.Tree (Tree)++-- contravariant+import Data.Functor.Contravariant+import Data.Functor.Contravariant.Compose as Contravariant+import Data.Functor.Contravariant.Divisible++-- profunctors+import Data.Profunctor as Pro+import Data.Profunctor.Cayley+import Data.Profunctor.Closed+import Data.Profunctor.Codensity+import Data.Profunctor.Composition+import Data.Profunctor.Ran+import Data.Profunctor.Tambara++-- semigroups+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.Semigroup as Semigroup (First(..), Last(..), Option(..))+import Data.Semigroup (Min(..), Max(..), Arg(..))++-- stm+import Control.Concurrent.STM (STM)++-- tagged+import Data.Tagged (Tagged(..))++-- transformers+import Control.Applicative.Backwards (Backwards(..))+import Control.Applicative.Lift (Lift(..))+import Control.Monad.Trans.Cont (ContT)+import Control.Monad.Trans.Error (ErrorT(..))+import Control.Monad.Trans.Except (ExceptT(..), runExceptT)+import Control.Monad.Trans.Identity (IdentityT, mapIdentityT)+import Control.Monad.Trans.List (ListT, mapListT)+import Control.Monad.Trans.Maybe (MaybeT, mapMaybeT)+import qualified Control.Monad.Trans.RWS.Lazy as Lazy (RWST(..))+import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST(..))+import Control.Monad.Trans.Reader (ReaderT, mapReaderT)+import qualified Control.Monad.Trans.State.Lazy as Lazy (StateT(..))+import qualified Control.Monad.Trans.State.Strict as Strict (StateT(..))+import qualified Control.Monad.Trans.Writer.Lazy as Lazy (WriterT, mapWriterT)+import qualified Control.Monad.Trans.Writer.Strict as Strict (WriterT, mapWriterT)+import qualified Data.Functor.Compose as Transformers (Compose(..))+import Data.Functor.Constant (Constant(..))+import Data.Functor.Product as Transformers (Product(..))+import Data.Functor.Reverse (Reverse(..))+import Data.Functor.Sum as Transformers (Sum(..))++-- unordered-containers+import Data.HashMap.Lazy (HashMap)++-------------------------------------------------------------------------------+-- The Invariant class+-------------------------------------------------------------------------------++-- | Any @* -> *@ type parametric in the argument permits an instance of+-- @Invariant@.+--+-- Instances should satisfy the following laws:+--+-- > invmap id id = id+-- > invmap f2 f2' . invmap f1 f1' = invmap (f2 . f1) (f1' . f2')+class Invariant f where+ invmap :: (a -> b) -> (b -> a) -> f a -> f b++-- | Every 'Functor' is also an 'Invariant' functor.+invmapFunctor :: Functor f => (a -> b) -> (b -> a) -> f a -> f b+invmapFunctor = flip $ const fmap++-- | Every 'Contravariant' functor is also an 'Invariant' functor.+invmapContravariant :: Contravariant f => (a -> b) -> (b -> a) -> f a -> f b+invmapContravariant = const contramap++-------------------------------------------------------------------------------+-- Invariant instances+-------------------------------------------------------------------------------++instance Invariant Maybe where invmap = invmapFunctor+instance Invariant [] where invmap = invmapFunctor+instance Invariant IO where invmap = invmapFunctor+instance Invariant (Strict.ST s) where invmap = invmapFunctor+instance Invariant (Lazy.ST s) where invmap = invmapFunctor+instance Invariant ReadP where invmap = invmapFunctor+instance Invariant ReadPrec where invmap = invmapFunctor+instance Invariant ((->) a) where invmap = invmapFunctor+instance Invariant (Either a) where invmap = invmapFunctor+instance Invariant ((,) a) where invmap = invmapFunctor+instance Invariant ((,,) a b) where invmap f _ ~(a, b, x) = (a, b, f x)+instance Invariant ((,,,) a b c) where+ invmap f _ ~(a, b, c, x) = (a, b, c, f x)+instance Invariant ((,,,,) a b c d) where+ invmap f _ ~(a, b, c, d, x) = (a, b, c, d, f x)++-- | from @Control.Applicative@+instance Invariant (Const a) where invmap = invmapFunctor+-- | from @Control.Applicative@+instance Invariant ZipList where invmap = invmapFunctor+-- | from @Control.Applicative@+instance Monad m => Invariant (WrappedMonad m) where invmap = invmapFunctor+-- | from @Control.Applicative@+instance Arrow arr => Invariant (App.WrappedArrow arr a) where+ invmap f _ (App.WrapArrow x) = App.WrapArrow $ ((arr f) Cat.. x)++-- | from @Control.Arrow@+instance+#if MIN_VERSION_base(4,4,0)+ Arrow a+#else+ ArrowApply a+#endif+ => Invariant (ArrowMonad a) where+ invmap f _ (ArrowMonad m) = ArrowMonad $ m >>> arr f++-- | from @Control.Exception@+instance Invariant Handler where+ invmap f _ (Handler h) = Handler (fmap f . h)++-- | from @Data.Functor.Identity@+instance Invariant Identity where+ invmap = invmapFunctor++-- | from @Data.Monoid@+instance Invariant Dual where invmap f _ (Dual x) = Dual (f x)+-- | from @Data.Monoid@+instance Invariant Endo where+ invmap f g (Endo x) = Endo (f . x . g)+-- | from @Data.Monoid@+instance Invariant Monoid.First where+ invmap f g (Monoid.First x) = Monoid.First (invmap f g x)+-- | from @Data.Monoid@+instance Invariant Monoid.Last where+ invmap f g (Monoid.Last x) = Monoid.Last (invmap f g x)+#if MIN_VERSION_base(4,8,0)+-- | from @Data.Monoid@+instance Invariant f => Invariant (Alt f) where+ invmap f g (Alt x) = Alt (invmap f g x)+#endif++-- | from @Data.Proxy@+instance Invariant Proxy where+ invmap = invmapFunctor++-- | from @System.Console.GetOpt@+instance Invariant ArgDescr where+ invmap f _ (NoArg a) = NoArg (f a)+ invmap f _ (ReqArg g s) = ReqArg (f . g) s+ invmap f _ (OptArg g s) = OptArg (f . g) s+-- | from @System.Console.GetOpt@+instance Invariant ArgOrder where+ invmap _ _ RequireOrder = RequireOrder+ invmap _ _ Permute = Permute+ invmap f _ (ReturnInOrder g) = ReturnInOrder (f . g)+-- | from @System.Console.GetOpt@+instance Invariant OptDescr where+ invmap f g (GetOpt.Option a b argDescr c) = GetOpt.Option a b (invmap f g argDescr) c++-- | from the @array@ package+instance+#if __GLASGOW_HASKELL__ < 711+ Ix i =>+#endif+ Invariant (Array i) where+ invmap = invmapFunctor++-- | from the @bifunctors@ package+instance (Invariant2 p, Invariant g) => Invariant (Biff p f g a) where+ invmap f g = Biff . invmap2 id id (invmap f g) (invmap g f) . runBiff+-- | from the @bifunctors@ package+instance Invariant (Clown f a) where+ invmap = invmapFunctor+-- | from the @bifunctors@ package+instance Invariant2 p => Invariant (Flip p a) where+ invmap = invmap2 id id+-- | from the @bifunctors@ package+instance Invariant2 p => Invariant (Join p) where+ invmap f g = Join . invmap2 f g f g . runJoin+-- | from the @bifunctors@ package+instance Invariant g => Invariant (Joker g a) where+ invmap = invmap2 id id+-- | from the @bifunctors@ package+instance (Invariant f, Invariant2 p) => Invariant (Tannen f p a) where+ invmap = invmap2 id id+-- | from the @bifunctors@ package+instance Bifunctor p => Invariant (WrappedBifunctor p a) where+ invmap = invmap2 id id++-- | from the @containers@ package+instance Invariant IntMap where+ invmap = invmapFunctor+-- | from the @containers@ package+instance Invariant (Map k) where+ invmap = invmapFunctor+-- | from the @containers@ package+instance Invariant Seq where+ invmap = invmapFunctor+-- | from the @containers@ package+instance Invariant ViewL where+ invmap = invmapFunctor+-- | from the @containers@ package+instance Invariant ViewR where+ invmap = invmapFunctor+-- | from the @containers@ package+instance Invariant Tree where+ invmap = invmapFunctor++-- | from the @contravariant@ package+instance Invariant Predicate where invmap = invmapContravariant+-- | from the @contravariant@ package+instance Invariant Comparison where invmap = invmapContravariant+-- | from the @contravariant@ package+instance Invariant Equivalence where invmap = invmapContravariant+-- | from the @contravariant@ package+instance Invariant (Op a) where invmap = invmapContravariant+-- | from the @contravariant@ package+instance (Invariant f, Invariant g) => Invariant (Contravariant.Compose f g) where+ invmap f g (Contravariant.Compose x) =+ Contravariant.Compose $ invmap (invmap f g) (invmap g f) x+-- | from the @contravariant@ package+instance (Invariant f, Invariant g) => Invariant (ComposeCF f g) where+ invmap f g (ComposeCF x) = ComposeCF $ invmap (invmap f g) (invmap g f) x+-- | from the @contravariant@ package+instance (Invariant f, Invariant g) => Invariant (ComposeFC f g) where+ invmap f g (ComposeFC x) = ComposeFC $ invmap (invmap f g) (invmap g f) x++-- | from the @profunctors@ package+instance Invariant f => Invariant (Star f a) where+ invmap = invmap2 id id+-- | from the @profunctors@ package+instance Invariant (Costar f a) where+ invmap = invmapFunctor+-- | from the @profunctors@ package+instance Arrow arr => Invariant (Pro.WrappedArrow arr a) where+ invmap f _ (Pro.WrapArrow x) = Pro.WrapArrow $ ((arr f) Cat.. x)+-- | from the @profunctors@ package+instance Invariant (Forget r a) where+ invmap = invmapFunctor+-- | from the @profunctors@ package+instance Invariant2 p => Invariant (Closure p a) where+ invmap = invmap2 id id+-- | from the @profunctors@ package+instance Invariant2 p => Invariant (Codensity p a) where+ invmap = invmap2 id id+-- | from the @profunctors@ package+instance Invariant2 p => Invariant (Procompose p q a) where+ invmap k k' (Procompose f g) = Procompose (invmap2 id id k k' f) g+-- | from the @profunctors@ package+instance Invariant2 p => Invariant (Rift p q a) where+ invmap bd db (Rift f) = Rift (f . invmap2 db bd id id)+-- | from the @profunctors@ package+instance Invariant2 q => Invariant (Ran p q a) where+ invmap bd db (Ran f) = Ran (invmap2 id id bd db . f)+-- | from the @profunctors@ package+instance Invariant2 p => Invariant (Tambara p a) where+ invmap = invmap2 id id+-- | from the @profunctors@ package+instance Invariant2 p => Invariant (Cotambara p a) where+ invmap = invmap2 id id++-- | from the @semigroups@ package+instance Invariant NonEmpty where+ invmap = invmapFunctor+-- | from the @semigroups@ package+instance Invariant Min where+ invmap = invmapFunctor+-- | from the @semigroups@ package+instance Invariant Max where+ invmap = invmapFunctor+-- | from the @semigroups@ package+instance Invariant Semigroup.First where+ invmap = invmapFunctor+-- | from the @semigroups@ package+instance Invariant Semigroup.Last where+ invmap = invmapFunctor+-- | from the @semigroups@ package+instance Invariant Semigroup.Option where+ invmap = invmapFunctor+-- | from the @semigroups@ package+instance Invariant (Arg a) where+ invmap = invmapFunctor++-- | from the @stm@ package+instance Invariant STM where+ invmap = invmapFunctor++-- | from the @tagged@ package+instance Invariant (Tagged s) where+ invmap = invmapFunctor++-- | from the @transformers@ package+instance Invariant f => Invariant (Backwards f) where+ invmap f g (Backwards a) = Backwards (invmap f g a)+-- | from the @transformers@ package+instance Invariant f => Invariant (Lift f) where+ invmap f _ (Pure x) = Pure (f x)+ invmap f g (Other y) = Other (invmap f g y)+-- | from the @transformers@ package+instance Invariant (ContT r m) where+ invmap = invmapFunctor+-- -- | from the @transformers@ package+instance Invariant m => Invariant (ErrorT e m) where+ invmap f g = ErrorT . invmap (invmap f g) (invmap g f) . runErrorT+-- | from the @transformers@ package+instance Invariant m => Invariant (ExceptT e m) where+ invmap f g = ExceptT . invmap (invmap f g) (invmap g f) . runExceptT+-- | from the @transformers@ package+instance Invariant m => Invariant (IdentityT m) where+ invmap f g = mapIdentityT (invmap f g)+-- | from the @transformers@ package+instance Invariant m => Invariant (ListT m) where+ invmap f g = mapListT $ invmap (invmap f g) (invmap g f)+-- | from the @transformers@ package+instance Invariant m => Invariant (MaybeT m) where+ invmap f g = mapMaybeT $ invmap (invmap f g) (invmap g f)+-- | from the @transformers@ package+instance Invariant m => Invariant (Lazy.RWST r w s m) where+ invmap f g m = Lazy.RWST $ \r s ->+ invmap (mapFstTriple f) (mapFstTriple g) $ Lazy.runRWST m r s+ where mapFstTriple h ~(a, s, w) = (h a, s, w)+-- | from the @transformers@ package+instance Invariant m => Invariant (Strict.RWST r w s m) where+ invmap f g m = Strict.RWST $ \r s ->+ invmap (mapFstTriple f) (mapFstTriple g) $ Strict.runRWST m r s+ where mapFstTriple h (a, s, w) = (h a, s, w)+-- | from the @transformers@ package+instance Invariant m => Invariant (ReaderT r m) where+ invmap f g = mapReaderT (invmap f g)+-- | from the @transformers@ package+instance Invariant m => Invariant (Lazy.StateT s m) where+ invmap f g m = Lazy.StateT $ \s ->+ invmap (mapFstPair f) (mapFstPair g) $ Lazy.runStateT m s+ where mapFstPair h ~(a, s) = (h a, s)+-- | from the @transformers@ package+instance Invariant m => Invariant (Strict.StateT s m) where+ invmap f g m = Strict.StateT $ \s ->+ invmap (mapFstPair f) (mapFstPair g) $ Strict.runStateT m s+ where mapFstPair h (a, s) = (h a, s)+-- | from the @transformers@ package+instance Invariant m => Invariant (Lazy.WriterT w m) where+ invmap f g = Lazy.mapWriterT $ invmap (mapFstPair f) (mapFstPair g)+ where mapFstPair h ~(a, w) = (h a, w)+-- | from the @transformers@ package+instance Invariant m => Invariant (Strict.WriterT w m) where+ invmap f g = Strict.mapWriterT $ invmap (mapFstPair f) (mapFstPair g)+ where mapFstPair h (a, w) = (h a, w)+-- | from the @transformers@ package+instance (Invariant f, Invariant g) => Invariant (Transformers.Compose f g) where+ invmap f g (Transformers.Compose x) =+ Transformers.Compose (invmap (invmap f g) (invmap g f) x)+-- | from the @transformers@ package+instance Invariant (Constant a) where+ invmap = invmapFunctor+-- | from the @transformers@ package+instance (Invariant f, Invariant g) => Invariant (Transformers.Product f g) where+ invmap f g (Transformers.Pair x y) = Transformers.Pair (invmap f g x) (invmap f g y)+-- | from the @transformers@ package+instance Invariant f => Invariant (Reverse f) where+ invmap f g (Reverse a) = Reverse (invmap f g a)+-- | from the @transformers@ package+instance (Invariant f, Invariant g) => Invariant (Transformers.Sum f g) where+ invmap f g (InL x) = InL (invmap f g x)+ invmap f g (InR y) = InR (invmap f g y)++-- | from the @unordered-containers@ package+instance Invariant (HashMap k) where+ invmap = invmapFunctor++-------------------------------------------------------------------------------+-- WrappedFunctor+-------------------------------------------------------------------------------++-- | Wrap a 'Functor' to be used as a member of 'Invariant'.+newtype WrappedFunctor f a = WrapFunctor { unwrapFunctor :: f a }+ deriving (Eq, Ord, Read, Show)++instance Functor f => Invariant (WrappedFunctor f) where+ invmap f g = WrapFunctor . invmapFunctor f g . unwrapFunctor++instance Functor f => Functor (WrappedFunctor f) where+ fmap f = WrapFunctor . fmap f . unwrapFunctor++instance Applicative f => Applicative (WrappedFunctor f) where+ pure = WrapFunctor . pure+ WrapFunctor f <*> WrapFunctor x = WrapFunctor $ f <*> x++instance Alternative f => Alternative (WrappedFunctor f) where+ empty = WrapFunctor empty+ WrapFunctor x <|> WrapFunctor y = WrapFunctor $ x <|> y++instance Monad m => Monad (WrappedFunctor m) where+ return = WrapFunctor . return+ WrapFunctor x >>= f = WrapFunctor $ x >>= unwrapFunctor . f++instance MonadPlus m => MonadPlus (WrappedFunctor m) where+ mzero = WrapFunctor mzero+ WrapFunctor x `mplus` WrapFunctor y = WrapFunctor $ x `mplus` y++-------------------------------------------------------------------------------+-- WrappedContravariant+-------------------------------------------------------------------------------++-- | Wrap a 'Contravariant' functor to be used as a member of 'Invariant'.+newtype WrappedContravariant f a = WrapContravariant { unwrapContravariant :: f a }+ deriving (Eq, Ord, Read, Show)++instance Contravariant f => Invariant (WrappedContravariant f) where+ invmap f g = WrapContravariant . invmapContravariant f g . unwrapContravariant++instance Contravariant f => Contravariant (WrappedContravariant f) where+ contramap f = WrapContravariant . contramap f . unwrapContravariant++instance Divisible f => Divisible (WrappedContravariant f) where+ divide f (WrapContravariant l) (WrapContravariant r) =+ WrapContravariant $ divide f l r+ conquer = WrapContravariant conquer++instance Decidable f => Decidable (WrappedContravariant f) where+ lose = WrapContravariant . lose+ choose f (WrapContravariant l) (WrapContravariant r) =+ WrapContravariant $ choose f l r++-------------------------------------------------------------------------------+-- The Invariant2 class+-------------------------------------------------------------------------------++-- | Any @* -> * -> *@ type parametric in both arguments permits an instance of+-- @Invariant2@.+--+-- Instances should satisfy the following laws:+--+-- > invmap2 id id id id = id+-- > invmap2 f2 f2' g2 g2' . invmap2 f1 f1' g1 g1' =+-- > invmap2 (f2 . f1) (f1' . f2') (g2 . g1) (g1' . g2')+class Invariant2 f where+ invmap2 :: (a -> c) -> (c -> a) -> (b -> d) -> (d -> b) -> f a b -> f c d++-- | Every 'Bifunctor' is also an 'Invariant2' functor.+invmap2Bifunctor :: Bifunctor f+ => (a -> c) -> (c -> a)+ -> (b -> d) -> (d -> b)+ -> f a b -> f c d+invmap2Bifunctor f _ g _ = bimap f g++-- | Every 'Profunctor' is also an 'Invariant2' functor.+invmap2Profunctor :: Profunctor f+ => (a -> c) -> (c -> a)+ -> (b -> d) -> (d -> b)+ -> f a b -> f c d+invmap2Profunctor _ f' g _ = dimap f' g++-------------------------------------------------------------------------------+-- Invariant2 instances+-------------------------------------------------------------------------------++instance Invariant2 (->) where invmap2 = invmap2Profunctor+instance Invariant2 Either where invmap2 = invmap2Bifunctor+instance Invariant2 (,) where invmap2 f _ g _ ~(x, y) = (f x, g y)+instance Invariant2 ((,,) a) where invmap2 f _ g _ ~(a, x, y) = (a, f x, g y)+instance Invariant2 ((,,,) a b) where+ invmap2 f _ g _ ~(a, b, x, y) = (a, b, f x, g y)+instance Invariant2 ((,,,,) a b c) where+ invmap2 f _ g _ ~(a, b, c, x, y) = (a, b, c, f x, g y)++-- | from @Control.Applicative@+instance Invariant2 Const where invmap2 = invmap2Bifunctor+-- | from @Control.Applicative@+instance Arrow arr => Invariant2 (App.WrappedArrow arr) where+ invmap2 _ f' g _ (App.WrapArrow x) = App.WrapArrow $ arr g Cat.. x Cat.. arr f'++-- | from the @bifunctors@ package+instance (Invariant2 p, Invariant f, Invariant g) => Invariant2 (Biff p f g) where+ invmap2 f f' g g' =+ Biff . invmap2 (invmap f f') (invmap f' f) (invmap g g') (invmap g' g) . runBiff+-- | from the @bifunctors@ package+instance Invariant f => Invariant2 (Clown f) where+ invmap2 f f' _ _ = Clown . invmap f f' . runClown+-- | from the @bifunctors@ package+instance Invariant2 p => Invariant2 (Flip p) where+ invmap2 f f' g g' = Flip . invmap2 g g' f f' . runFlip+-- | from the @bifunctors@ package+instance Invariant g => Invariant2 (Joker g) where+ invmap2 _ _ g g' = Joker . invmap g g' . runJoker+-- | from the @bifunctors@ package+instance (Invariant2 f, Invariant2 g) => Invariant2 (Bifunctors.Product f g) where+ invmap2 f f' g g' (Bifunctors.Pair x y) =+ Bifunctors.Pair (invmap2 f f' g g' x) (invmap2 f f' g g' y)+-- | from the @bifunctors@ package+instance (Invariant f, Invariant2 p) => Invariant2 (Tannen f p) where+ invmap2 f f' g g' =+ Tannen . invmap (invmap2 f f' g g') (invmap2 f' f g' g) . runTannen+-- | from the @bifunctors@ package+instance Bifunctor p => Invariant2 (WrappedBifunctor p) where+ invmap2 f f' g g' = WrapBifunctor . invmap2Bifunctor f f' g g' . unwrapBifunctor++-- | from the @contravariant@ package+instance Invariant2 Op where+ invmap2 f f' g g' (Op x) = Op $ invmap2 g g' f f' x++-- | from the @profunctors@ package+instance Invariant f => Invariant2 (Star f) where+ invmap2 _ ba cd dc (Star afc) = Star $ invmap cd dc . afc . ba+-- | from the @profunctors@ package+instance Invariant f => Invariant2 (Costar f) where+ invmap2 ab ba cd _ (Costar fbc) = Costar $ cd . fbc . invmap ba ab+-- | from the @profunctors@ package+instance Arrow arr => Invariant2 (Pro.WrappedArrow arr) where+ invmap2 _ f' g _ (Pro.WrapArrow x) = Pro.WrapArrow $ arr g Cat.. x Cat.. arr f'+-- | from the @profunctors@ package+instance Invariant2 (Forget r) where+ invmap2 = invmap2Profunctor+-- | from the @profunctors@ package+instance (Invariant f, Invariant2 p) => Invariant2 (Cayley f p) where+ invmap2 f f' g g' =+ Cayley . invmap (invmap2 f f' g g') (invmap2 f' f g' g) . runCayley+-- | from the @profunctors@ package+instance Invariant2 p => Invariant2 (Closure p) where+ invmap2 f f' g g' (Closure p) = Closure $ invmap2 (f .) (f' .) (g .) (g' .) p+-- | from the @profunctors@ package+instance Invariant2 (Environment p) where+ invmap2 _ f' g _ (Environment l m r) = Environment (g . l) m (r . f')+-- | from the @profunctors@ package+instance Invariant2 p => Invariant2 (Codensity p) where+ invmap2 ac ca bd db (Codensity f) =+ Codensity (invmap2 id id bd db . f . invmap2 id id ca ac)+-- | from the @profunctors@ package+instance (Invariant2 p, Invariant2 q) => Invariant2 (Procompose p q) where+ invmap2 l l' r r' (Procompose f g) =+ Procompose (invmap2 id id r r' f) (invmap2 l l' id id g)+-- | from the @profunctors@ package+instance (Invariant2 p, Invariant2 q) => Invariant2 (Rift p q) where+ invmap2 ac ca bd db (Rift f) = Rift (invmap2 ac ca id id . f . invmap2 db bd id id)+-- | from the @profunctors@ package+instance (Invariant2 p, Invariant2 q) => Invariant2 (Ran p q) where+ invmap2 ac ca bd db (Ran f) = Ran (invmap2 id id bd db . f . invmap2 id id ca ac)+-- | from the @profunctors@ package+instance Invariant2 p => Invariant2 (Tambara p) where+ invmap2 f f' g g' (Tambara p) =+ Tambara $ invmap2 (first f) (first f') (first g) (first g') p+-- | from the @profunctors@ package+instance Invariant2 (Pastro p) where+ invmap2 _ f' g _ (Pastro l m r) = Pastro (g . l) m (r . f')+-- | from the @profunctors@ package+instance Invariant2 p => Invariant2 (Cotambara p) where+ invmap2 f f' g g' (Cotambara p) =+ Cotambara $ invmap2 (left f) (left f') (left g) (left g') p+-- | from the @profunctors@ package+instance Invariant2 (Copastro p) where+ invmap2 _ f' g _ (Copastro l m r) = Copastro (g . l) m (r . f')++-- | from the @semigroups@ package+instance Invariant2 Arg where+ invmap2 = invmap2Bifunctor++-- | from the @tagged@ package+instance Invariant2 Tagged where+ invmap2 = invmap2Bifunctor++-- | from the @transformers@ package+instance Invariant2 Constant where+ invmap2 f _ _ _ (Constant x) = Constant (f x)++-------------------------------------------------------------------------------+-- WrappedProfunctor+-------------------------------------------------------------------------------++-- | Wrap a 'Profunctor' to be used as a member of 'Invariant2'.+newtype WrappedProfunctor p a b = WrapProfunctor { unwrapProfunctor :: p a b }+ deriving (Eq, Ord, Read, Show)++instance Profunctor p => Invariant2 (WrappedProfunctor p) where+ invmap2 f f' g g' = WrapProfunctor . invmap2Profunctor f f' g g' . unwrapProfunctor++instance Profunctor p => Invariant (WrappedProfunctor p a) where+ invmap = invmap2 id id++instance Profunctor p => Profunctor (WrappedProfunctor p) where+ dimap f g = WrapProfunctor . dimap f g . unwrapProfunctor++instance Strong p => Strong (WrappedProfunctor p) where+ first' = WrapProfunctor . first' . unwrapProfunctor+ second' = WrapProfunctor . second' . unwrapProfunctor++instance Choice p => Choice (WrappedProfunctor p) where+ left' = WrapProfunctor . left' . unwrapProfunctor+ right' = WrapProfunctor . right' . unwrapProfunctor++instance Costrong p => Costrong (WrappedProfunctor p) where+ unfirst = WrapProfunctor . unfirst . unwrapProfunctor+ unsecond = WrapProfunctor . unsecond . unwrapProfunctor++instance Cochoice p => Cochoice (WrappedProfunctor p) where+ unleft = WrapProfunctor . unleft . unwrapProfunctor+ unright = WrapProfunctor . unright . unwrapProfunctor++instance Closed p => Closed (WrappedProfunctor p) where+ closed = WrapProfunctor . closed . unwrapProfunctor++#if GHC_GENERICS_OK+-------------------------------------------------------------------------------+-- GHC Generics+-------------------------------------------------------------------------------++-- | from @GHC.Generics@+instance Invariant V1 where+ -- NSF 25 July 2015: I'd prefer an -XEmptyCase, but Haskell98.+ invmap _ _ _ = error "Invariant V1"+-- | from @GHC.Generics@+instance Invariant U1 where invmap _ _ _ = U1+-- | from @GHC.Generics@+instance (Invariant l, Invariant r) => Invariant ((:+:) l r) where+ invmap f g (L1 l) = L1 $ invmap f g l+ invmap f g (R1 r) = R1 $ invmap f g r+-- | from @GHC.Generics@+instance (Invariant l, Invariant r) => Invariant ((:*:) l r) where+ invmap f g ~(l :*: r) = invmap f g l :*: invmap f g r+-- | from @GHC.Generics@+instance Invariant (K1 i c) where invmap _ _ (K1 c) = K1 c+-- | from @GHC.Generics@+instance Invariant2 (K1 i) where invmap2 f _ _ _ (K1 c) = K1 $ f c+-- | from @GHC.Generics@+instance Invariant f => Invariant (M1 i t f) where invmap f g (M1 fp) = M1 $ invmap f g fp+-- | from @GHC.Generics@+instance Invariant Par1 where invmap f _ (Par1 c) = Par1 $ f c+-- | from @GHC.Generics@+instance Invariant f => Invariant (Rec1 f) where invmap f g (Rec1 fp) = Rec1 $ invmap f g fp+-- | from @GHC.Generics@; genuinely relying on this instance+-- likely requires writing your 'Generic1' instance by hand+instance (Invariant f, Invariant g) => Invariant ((:.:) f g) where+ invmap f g (Comp1 fgp) = Comp1 $ invmap (invmap f g) (invmap g f) fgp+++{- $ghcgenerics++Note: The restriction to Haskell98 prevents the full adoption of+"GHC.Generics", but we are permitted to at least provide @Invariant@+instances for the representation data types. Thus, while the \"ideal\"++@+ instance Invariant f => 'Invariant' (T f)+@++doesn't work --- because Haskell98 precludes our use of+@-XDefaultSignatures@ in the class definition ---, the user only needs+to do slightly more work:++@+ import GHC.Generics (from1,to1)++ instance Invariant f => 'Invariant' (T f) where+ invmap f g = 'to1' . 'invmap' f g . 'from1'+@++Note also that that instance is in fact Haskell98. Unfortunately, one+would require @-XFlexibleContexts@ in order to factor that right-hand+side out as reusable declaration polymorphic in the data type.+-}+#endif
+ src/Data/Functor/Invariant/TH.hs view
@@ -0,0 +1,785 @@+{-# LANGUAGE CPP #-}++{-|+Module: Data.Functor.Invariant.TH+Copyright: (C) 2012-2015 Nicolas Frisby, (C) 2015 Ryan Scott+License: BSD-style (see the file LICENSE)+Maintainer: Ryan Scott+Portability: Template Haskell++Functions to mechanically derive 'Invariant' or 'Invariant2' instances,+or to splice 'invmap' or 'invmap2' into Haskell source code. You need to enable+the @TemplateHaskell@ language extension in order to use this module.+-}+module Data.Functor.Invariant.TH (+ -- * @deriveInvariant(2)@+ -- $deriveInvariant+ deriveInvariant+ -- $deriveInvariant2+ , deriveInvariant2+ -- * @makeInvmap(2)@+ -- $make+ , makeInvmap+ , makeInvmap2+ ) where++import Data.Functor.Invariant.TH.Internal+import Data.List+#if __GLASGOW_HASKELL__ < 710 && MIN_VERSION_template_haskell(2,8,0)+import qualified Data.Set as Set+#endif++import Language.Haskell.TH.Lib+import Language.Haskell.TH.Ppr+import Language.Haskell.TH.Syntax++-------------------------------------------------------------------------------+-- User-facing API+-------------------------------------------------------------------------------++{- $deriveInvariant++'deriveInvariant' automatically generates an 'Invariant' instance declaration for a+data type, newtype, or data family instance that has at least one type variable.+This emulates what would (hypothetically) happen if you could attach a @deriving+'Invariant'@ clause to the end of a data declaration. Examples:++@+{-# LANGUAGE TemplateHaskell #-}+import Data.Functor.Invariant.TH++data Pair a = Pair a a+$('deriveInvariant' ''Pair) -- instance Invariant Pair where ...++newtype Alt f a = Alt (f a)+$('deriveInvariant' ''Alt) -- instance Invariant f => Invariant (Alt f) where ...+@++If you are using @template-haskell-2.7.0.0@ or later (i.e., GHC 7.4 or later),+'deriveInvariant' can also be used to derive 'Invariant' instances for data family+instances (which requires the @-XTypeFamilies@ extension). To do so, pass the name of+a data or newtype instance constructor to 'deriveInvariant'. Note that the generated+code may require the @-XFlexibleInstances@ extension. Some examples:++@+{-# LANGUAGE FlexibleInstances, TemplateHaskell, TypeFamilies #-}+import Data.Functor.Invariant.TH++class AssocClass a b where+ data AssocData a b+instance AssocClass Int b where+ data AssocData Int b = AssocDataInt1 Int | AssocDataInt2 b Int+$('deriveInvariant' 'AssocDataInt1) -- instance Invariant (AssocData Int) where ...+-- Alternatively, one could use $(deriveInvariant 'AssocDataInt2)++data family DataFam a b+newtype instance DataFam () b = DataFamB b+$('deriveInvariant' 'DataFamB) -- instance Invariant (DataFam ())+@++Note that there are some limitations:++* The 'Name' argument to 'deriveInvariant' must not be a type synonym.++* With 'deriveInvariant', the argument's last type variable must be of kind @*@.+ For other ones, type variables of kind @* -> *@ are assumed to require an 'Invariant'+ context. For more complicated scenarios, use 'makeInvmap'.++* If using the @-XDatatypeContexts@, @-XExistentialQuantification@, or @-XGADTs@+ extensions, a constraint cannot mention the last type variable. For example,+ @data Illegal a where I :: Ord a => a -> Illegal a@ cannot have a derived+ 'Invariant' instance.++* If the last type variable is used within a data field of a constructor, it must only+ be used in the last argument of the data type constructor. For example, @data Legal a+ = Legal (Either Int a)@ can have a derived 'Invariant' instance, but @data Illegal a =+ Illegal (Either a a)@ cannot.++* Data family instances must be able to eta-reduce the last type variable. In other+ words, if you have a instance of the form:++ @+ data family Family a1 ... an t+ data instance Family e1 ... e2 v = ...+ @++ Then the following conditions must hold:++ 1. @v@ must be a type variable.+ 2. @v@ must not be mentioned in any of @e1@, ..., @e2@.++* In GHC 7.8, a bug exists that can cause problems when a data family declaration and+ one of its data instances use different type variables, e.g.,++ @+ data family Foo a b c+ data instance Foo Int y z = Foo Int y z+ $('deriveInvariant' 'Foo)+ @++ To avoid this issue, it is recommened that you use the same type variables in the+ same positions in which they appeared in the data family declaration:++ @+ data family Foo a b c+ data instance Foo Int b c = Foo Int b c+ $('deriveInvariant' 'Foo)+ @++-}++-- | Generates an 'Invariant' instance declaration for the given data type or data+-- family instance.+deriveInvariant :: Name -> Q [Dec]+deriveInvariant = deriveInvariantClass Invariant++{- $deriveInvariant2++'deriveInvariant2' automatically generates an 'Invariant2' instance declaration for+a data type, newtype, or data family instance that has at least two type variables.+This emulates what would (hypothetically) happen if you could attach a @deriving+'Invariant2'@ clause to the end of a data declaration. Examples:++@+{-# LANGUAGE TemplateHaskell #-}+import Data.Functor.Invariant.TH++data OneOrNone a b = OneL a | OneR b | None+$('deriveInvariant2' ''OneOrNone) -- instance Invariant2 OneOrNone where ...++newtype Alt2 f a b = Alt2 (f a b)+$('deriveInvariant2' ''Alt2) -- instance Invariant2 f => Invariant2 (Alt2 f) where ...+@++The same restrictions that apply to 'deriveInvariant' also apply to 'deriveInvariant2',+with some caveats:++* With 'deriveInvariant2', the last type variables must both be of kind @*@. For other+ ones, type variables of kind @* -> *@ are assumed to require an 'Invariant'+ constraint, and type variables of kind @* -> * -> *@ are assumed to require an+ 'Invariant2' constraint. For more complicated scenarios, use 'makeInvmap2'.++* If using the @-XDatatypeContexts@, @-XExistentialQuantification@, or @-XGADTs@+ extensions, a constraint cannot mention either of the last two type variables. For+ example, @data Illegal2 a b where I2 :: Ord a => a -> b -> Illegal2 a b@ cannot+ have a derived 'Invariant2' instance.++* If either of the last two type variables is used within a data field of a constructor,+ it must only be used in the last two arguments of the data type constructor. For+ example, @data Legal a b = Legal (Int, Int, a, b)@ can have a derived 'Invariant2'+ instance, but @data Illegal a b = Illegal (a, b, a, b)@ cannot.++* Data family instances must be able to eta-reduce the last two type variables. In other+ words, if you have a instance of the form:++ @+ data family Family a1 ... an t1 t2+ data instance Family e1 ... e2 v1 v2 = ...+ @++ Then the following conditions must hold:++ 1. @v1@ and @v2@ must be distinct type variables.+ 2. Neither @v1@ not @v2@ must be mentioned in any of @e1@, ..., @e2@.++-}++-- | Generates an 'Invariant2' instance declaration for the given data type or data+-- family instance.+deriveInvariant2 :: Name -> Q [Dec]+deriveInvariant2 = deriveInvariantClass Invariant2++{- $make++There may be scenarios in which you want to @invmap@ over an arbitrary data type or+data family instance without having to make the type an instance of 'Invariant'. For+these cases, this module provides several functions (all prefixed with @make-@) that+splice the appropriate lambda expression into your source code. Example:++This is particularly useful for creating instances for sophisticated data types. For+example, 'deriveInvariant' cannot infer the correct type context for @newtype+HigherKinded f a b c = HigherKinded (f a b c)@, since @f@ is of kind+@* -> * -> * -> *@. However, it is still possible to create an 'Invariant' instance+for @HigherKinded@ without too much trouble using 'makeInvmap':++@+{-# LANGUAGE FlexibleContexts, TemplateHaskell #-}+import Data.Functor.Invariant+import Data.Functor.Invariant.TH++newtype HigherKinded f a b c = HigherKinded (f a b c)++instance Invariant (f a b) => Invariant (HigherKinded f a b) where+ invmap = $(makeInvmap ''HigherKinded)+@++-}++-- | Generates a lambda expression which behaves like 'invmap' (without requiring an+-- 'Invariant' instance).+makeInvmap :: Name -> Q Exp+makeInvmap = makeInvmapClass Invariant++-- | Generates a lambda expression which behaves like 'invmap2' (without requiring an+-- 'Invariant2' instance).+makeInvmap2 :: Name -> Q Exp+makeInvmap2 = makeInvmapClass Invariant2++-------------------------------------------------------------------------------+-- Code generation+-------------------------------------------------------------------------------++-- | Derive an Invariant(2) instance declaration (depending on the InvariantClass+-- argument's value).+deriveInvariantClass :: InvariantClass -> Name -> Q [Dec]+deriveInvariantClass iClass tyConName = do+ info <- reify tyConName+ case info of+ TyConI{} -> deriveInvariantPlainTy iClass tyConName+#if MIN_VERSION_template_haskell(2,7,0)+ DataConI{} -> deriveInvariantDataFamInst iClass tyConName+ FamilyI (FamilyD DataFam _ _ _) _ ->+ error $ ns ++ "Cannot use a data family name. Use a data family instance constructor instead."+ FamilyI (FamilyD TypeFam _ _ _) _ ->+ error $ ns ++ "Cannot use a type family name."+ _ -> error $ ns ++ "The name must be of a plain type constructor or data family instance constructor."+#else+ DataConI{} -> dataConIError+ _ -> error $ ns ++ "The name must be of a plain type constructor."+#endif+ where+ ns :: String+ ns = "Data.Functor.Invariant.TH.deriveInvariant: "++-- | Generates an Invariant(2) instance declaration for a plain type constructor.+deriveInvariantPlainTy :: InvariantClass -> Name -> Q [Dec]+deriveInvariantPlainTy iClass tyConName =+ withTyCon tyConName fromCons+ where+ className :: Name+ className = invariantClassNameTable iClass++ fromCons :: Cxt -> [TyVarBndr] -> [Con] -> Q [Dec]+ fromCons ctxt tvbs cons = (:[]) `fmap`+ instanceD (return instanceCxt)+ (return $ AppT (ConT className) instanceType)+ (invmapDecs droppedNbs cons)+ where+ (instanceCxt, instanceType, droppedNbs) =+ cxtAndTypePlainTy iClass tyConName ctxt tvbs++#if MIN_VERSION_template_haskell(2,7,0)+-- | Generates an Invariant(2) instance declaration for a data family instance+-- constructor.+deriveInvariantDataFamInst :: InvariantClass -> Name -> Q [Dec]+deriveInvariantDataFamInst iClass dataFamInstName =+ withDataFamInstCon dataFamInstName fromDec+ where+ className :: Name+ className = invariantClassNameTable iClass++ fromDec :: [TyVarBndr] -> Cxt -> Name -> [Type] -> [Con] -> Q [Dec]+ fromDec famTvbs ctxt parentName instTys cons = (:[]) `fmap`+ instanceD (return instanceCxt)+ (return $ AppT (ConT className) instanceType)+ (invmapDecs droppedNbs cons)+ where+ (instanceCxt, instanceType, droppedNbs) =+ cxtAndTypeDataFamInstCon iClass parentName ctxt famTvbs instTys+#endif++-- | Generates a declaration defining the primary function corresponding to a+-- particular class (invmap for Invariant and invmap2 for Invariant2).+invmapDecs :: [NameBase] -> [Con] -> [Q Dec]+invmapDecs nbs cons =+ [ funD classFuncName+ [ clause []+ (normalB $ makeInvmapForCons nbs cons)+ []+ ]+ ]+ where+ classFuncName :: Name+ classFuncName = invmapNameTable . toEnum $ length nbs++-- | Generates a lambda expression which behaves like invmap (for Invariant),+-- or invmap2 (for Invariant2).+makeInvmapClass :: InvariantClass -> Name -> Q Exp+makeInvmapClass iClass tyConName = do+ info <- reify tyConName+ case info of+ TyConI{} -> withTyCon tyConName $ \ctxt tvbs decs ->+ let nbs = thd3 $ cxtAndTypePlainTy iClass tyConName ctxt tvbs+ in nbs `seq` makeInvmapForCons nbs decs+#if MIN_VERSION_template_haskell(2,7,0)+ DataConI{} -> withDataFamInstCon tyConName $ \famTvbs ctxt parentName instTys cons ->+ let nbs = thd3 $ cxtAndTypeDataFamInstCon iClass parentName ctxt famTvbs instTys+ in nbs `seq` makeInvmapForCons nbs cons+ FamilyI (FamilyD DataFam _ _ _) _ ->+ error $ ns ++ "Cannot use a data family name. Use a data family instance constructor instead."+ FamilyI (FamilyD TypeFam _ _ _) _ ->+ error $ ns ++ "Cannot use a type family name."+ _ -> error $ ns ++ "The name must be of a plain type constructor or data family instance constructor."+#else+ DataConI{} -> dataConIError+ _ -> error $ ns ++ "The name must be of a plain type constructor."+#endif+ where+ ns :: String+ ns = "Data.Functor.Invariant.TH.makeInvmap: "++-- | Generates a lambda expression for invmap(2) for the given constructors.+-- All constructors must be from the same type.+makeInvmapForCons :: [NameBase] -> [Con] -> Q Exp+makeInvmapForCons nbs cons = do+ let numNbs = length nbs++ value <- newName "value"+ covMaps <- newNameList "covMap" numNbs+ contraMaps <- newNameList "contraMap" numNbs++ let tvis = zip3 nbs covMaps contraMaps+ iClass = toEnum numNbs+ argNames = concat (transpose [covMaps, contraMaps]) ++ [value]+ lamE (map varP argNames)+ . appsE+ $ [ varE $ invmapConstNameTable iClass+ , if null cons+ then appE (varE errorValName)+ (stringE $ "Void " ++ nameBase (invmapNameTable iClass))+ else caseE (varE value)+ (map (makeInvmapForCon iClass tvis) cons)+ ] ++ map varE argNames++-- | Generates a lambda expression for invmap(2) for a single constructor.+makeInvmapForCon :: InvariantClass -> [TyVarInfo] -> Con -> Q Match+makeInvmapForCon iClass tvis (NormalC conName tys) = do+ args <- newNameList "arg" $ length tys+ let argTys = map snd tys+ makeInvmapForArgs iClass tvis conName argTys args+makeInvmapForCon iClass tvis (RecC conName tys) = do+ args <- newNameList "arg" $ length tys+ let argTys = map thd3 tys+ makeInvmapForArgs iClass tvis conName argTys args+makeInvmapForCon iClass tvis (InfixC (_, argTyL) conName (_, argTyR)) = do+ argL <- newName "argL"+ argR <- newName "argR"+ makeInvmapForArgs iClass tvis conName [argTyL, argTyR] [argL, argR]+makeInvmapForCon iClass tvis (ForallC tvbs faCxt con) =+ if any (`predMentionsNameBase` map fst3 tvis) faCxt+ then existentialContextError $ constructorName con+ else makeInvmapForCon iClass (removeForalled tvbs tvis) con++makeInvmapForArgs :: InvariantClass+ -> [TyVarInfo]+ -> Name+ -> [Type]+ -> [Name]+ -> Q Match+makeInvmapForArgs iClass tvis conName tys args =+ let mappedArgs :: [Q Exp]+ mappedArgs = zipWith (makeInvmapForArg iClass conName tvis) tys args+ in match (conP conName $ map varP args)+ (normalB . appsE $ conE conName:mappedArgs)+ []++-- | Generates a lambda expression for invmap(2) for an argument of a constructor.+makeInvmapForArg :: InvariantClass+ -> Name+ -> [TyVarInfo]+ -> Type+ -> Name+ -> Q Exp+makeInvmapForArg iClass conName tvis ty tyExpName = do+ ty' <- expandSyn ty+ makeInvmapForArg' iClass conName tvis ty' tyExpName++-- | Generates a lambda expression for invmap(2) for an argument of a+-- constructor, after expanding all type synonyms.+makeInvmapForArg' :: InvariantClass+ -> Name+ -> [TyVarInfo]+ -> Type+ -> Name+ -> Q Exp+makeInvmapForArg' iClass conName tvis ty tyExpName =+ appE (makeInvmapForType iClass conName tvis True ty) (varE tyExpName)++-- | Generates a lambda expression for invmap(2) for a specific type.+-- The generated expression depends on the number of type variables.+makeInvmapForType :: InvariantClass+ -> Name+ -> [TyVarInfo]+ -> Bool+ -> Type+ -> Q Exp+makeInvmapForType _ _ tvis covariant (VarT tyName) =+ case lookup2 (NameBase tyName) tvis of+ Just (covMap, contraMap) ->+ varE $ if covariant then covMap else contraMap+ Nothing -> do -- Produce a lambda expression rather than id, addressing Trac #7436+ x <- newName "x"+ lamE [varP x] $ varE x+makeInvmapForType iClass conName tvis covariant (SigT ty _) =+ makeInvmapForType iClass conName tvis covariant ty+makeInvmapForType iClass conName tvis covariant (ForallT tvbs _ ty)+ = makeInvmapForType iClass conName (removeForalled tvbs tvis) covariant ty+makeInvmapForType iClass conName tvis covariant ty =+ let tyCon :: Type+ tyArgs :: [Type]+ tyCon:tyArgs = unapplyTy ty++ numLastArgs :: Int+ numLastArgs = min (fromEnum iClass) (length tyArgs)++ lhsArgs, rhsArgs :: [Type]+ (lhsArgs, rhsArgs) = splitAt (length tyArgs - numLastArgs) tyArgs++ tyVarNameBases :: [NameBase]+ tyVarNameBases = map fst3 tvis++ doubleMap :: (Bool -> Type -> Q Exp) -> [Type] -> [Q Exp]+ doubleMap _ [] = []+ doubleMap f (t:ts) = f covariant t : f (not covariant) t : doubleMap f ts++ mentionsTyArgs :: Bool+ mentionsTyArgs = any (`mentionsNameBase` tyVarNameBases) tyArgs++ makeInvmapTuple :: Type -> Name -> Q Exp+ makeInvmapTuple fieldTy fieldName =+ appE (makeInvmapForType iClass conName tvis covariant fieldTy) $ varE fieldName++ in case tyCon of+ ArrowT | mentionsTyArgs ->+ let [argTy, resTy] = tyArgs+ in do x <- newName "x"+ b <- newName "b"+ lamE [varP x, varP b] $+ makeInvmapForType iClass conName tvis covariant resTy `appE` (varE x `appE`+ (makeInvmapForType iClass conName tvis (not covariant) argTy `appE` varE b))+ TupleT n | n > 0 && mentionsTyArgs -> do+ x <- newName "x"+ xs <- newNameList "x" n+ lamE [varP x] $ caseE (varE x)+ [ match (tupP $ map varP xs)+ (normalB . tupE $ zipWith makeInvmapTuple tyArgs xs)+ []+ ]+ _ -> do+ itf <- isTyFamily tyCon+ if any (`mentionsNameBase` tyVarNameBases) lhsArgs || (itf && mentionsTyArgs)+ then outOfPlaceTyVarError conName tyVarNameBases+ else if any (`mentionsNameBase` tyVarNameBases) rhsArgs+ then appsE $+ ( varE (invmapNameTable (toEnum numLastArgs))+ : doubleMap (makeInvmapForType iClass conName tvis) rhsArgs+ )+ else do x <- newName "x"+ lamE [varP x] $ varE x++-------------------------------------------------------------------------------+-- Template Haskell reifying and AST manipulation+-------------------------------------------------------------------------------++-- | Extracts a plain type constructor's information.+withTyCon :: Name -- ^ Name of the plain type constructor+ -> (Cxt -> [TyVarBndr] -> [Con] -> Q a)+ -> Q a+withTyCon name f = do+ info <- reify name+ case info of+ TyConI dec ->+ case dec of+ DataD ctxt _ tvbs cons _ -> f ctxt tvbs cons+ NewtypeD ctxt _ tvbs con _ -> f ctxt tvbs [con]+ other -> error $ ns ++ "Unsupported type " ++ show other ++ ". Must be a data type or newtype."+ _ -> error $ ns ++ "The name must be of a plain type constructor."+ where+ ns :: String+ ns = "Data.Functor.Invariant.TH.withTyCon: "++#if MIN_VERSION_template_haskell(2,7,0)+-- | Extracts a data family name's information.+withDataFam :: Name -- ^ Name of the data family+ -> ([TyVarBndr] -> [Dec] -> Q a)+ -> Q a+withDataFam name f = do+ info <- reify name+ case info of+ FamilyI (FamilyD DataFam _ tvbs _) decs -> f tvbs decs+ FamilyI (FamilyD TypeFam _ _ _) _ ->+ error $ ns ++ "Cannot use a type family name."+ other -> error $ ns ++ "Unsupported type " ++ show other ++ ". Must be a data family name."+ where+ ns :: String+ ns = "Data.Functor.Invariant.TH.withDataFam: "++-- | Extracts a data family instance constructor's information.+withDataFamInstCon :: Name -- ^ Name of the data family instance constructor+ -> ([TyVarBndr] -> Cxt -> Name -> [Type] -> [Con] -> Q a)+ -> Q a+withDataFamInstCon dficName f = do+ dficInfo <- reify dficName+ case dficInfo of+ DataConI _ _ parentName _ -> do+ parentInfo <- reify parentName+ case parentInfo of+ FamilyI (FamilyD DataFam _ _ _) _ -> withDataFam parentName $ \famTvbs decs ->+ let sameDefDec = flip find decs $ \dec ->+ case dec of+ DataInstD _ _ _ cons' _ -> any ((dficName ==) . constructorName) cons'+ NewtypeInstD _ _ _ con _ -> dficName == constructorName con+ _ -> error $ ns ++ "Must be a data or newtype instance."++ (ctxt, instTys, cons) = case sameDefDec of+ Just (DataInstD ctxt' _ instTys' cons' _) -> (ctxt', instTys', cons')+ Just (NewtypeInstD ctxt' _ instTys' con _) -> (ctxt', instTys', [con])+ _ -> error $ ns ++ "Could not find data or newtype instance constructor."++ in f famTvbs ctxt parentName instTys cons+ _ -> error $ ns ++ "Data constructor " ++ show dficName ++ " is not from a data family instance."+ other -> error $ ns ++ "Unsupported type " ++ show other ++ ". Must be a data family instance constructor."+ where+ ns :: String+ ns = "Data.Functor.Invariant.TH.withDataFamInstCon: "+#endif++-- | Deduces the Invariant(2) instance context, instance head, and eta-reduced+-- type variables for a plain data type constructor.+cxtAndTypePlainTy :: InvariantClass -- Invariant or Invariant2+ -> Name -- The datatype's name+ -> Cxt -- The datatype context+ -> [TyVarBndr] -- The type variables+ -> (Cxt, Type, [NameBase])+cxtAndTypePlainTy iClass tyConName dataCxt tvbs =+ if remainingLength < 0 || not (wellKinded droppedKinds) -- If we have enough well-kinded type variables+ then derivingKindError iClass tyConName+ else if any (`predMentionsNameBase` droppedNbs) dataCxt -- If the last type variable(s) are mentioned in a datatype context+ then datatypeContextError tyConName instanceType+ else (instanceCxt, instanceType, droppedNbs)+ where+ instanceCxt :: Cxt+ instanceCxt = map (applyInvariantConstraint)+ $ filter (needsConstraint iClass . tvbKind) remaining++ instanceType :: Type+ instanceType = applyTyCon tyConName $ map (VarT . tvbName) remaining++ remainingLength :: Int+ remainingLength = length tvbs - fromEnum iClass++ remaining, dropped :: [TyVarBndr]+ (remaining, dropped) = splitAt remainingLength tvbs++ droppedKinds :: [Kind]+ droppedKinds = map tvbKind dropped++ droppedNbs :: [NameBase]+ droppedNbs = map (NameBase . tvbName) dropped++#if MIN_VERSION_template_haskell(2,7,0)+-- | Deduces the Invariant(2) instance context, instance head, and eta-reduced+-- type variables for a data family instnce constructor.+cxtAndTypeDataFamInstCon :: InvariantClass -- Invariant or Invariant2+ -> Name -- The data family name+ -> Cxt -- The datatype context+ -> [TyVarBndr] -- The data family declaration's type variables+ -> [Type] -- The data family instance types+ -> (Cxt, Type, [NameBase])+cxtAndTypeDataFamInstCon iClass parentName dataCxt famTvbs instTysAndKinds =+ if remainingLength < 0 || not (wellKinded droppedKinds) -- If we have enough well-kinded type variables+ then derivingKindError iClass parentName+ else if any (`predMentionsNameBase` droppedNbs) dataCxt -- If the last type variable(s) are mentioned in a datatype context+ then datatypeContextError parentName instanceType+ else if canEtaReduce remaining dropped -- If it is safe to drop the type variables+ then (instanceCxt, instanceType, droppedNbs)+ else etaReductionError instanceType+ where+ instanceCxt :: Cxt+ instanceCxt = map (applyInvariantConstraint)+ $ filter (needsConstraint iClass . tvbKind) lhsTvbs++ -- We need to make sure that type variables in the instance head which have+ -- Invariant(2) constraints aren't poly-kinded, e.g.,+ --+ -- @+ -- instance Invariant f => Invariant (Foo (f :: k)) where+ -- @+ --+ -- To do this, we remove every kind ascription (i.e., strip off every 'SigT').+ instanceType :: Type+ instanceType = applyTyCon parentName+ $ map unSigT remaining++ remainingLength :: Int+ remainingLength = length famTvbs - fromEnum iClass++ remaining, dropped :: [Type]+ (remaining, dropped) = splitAt remainingLength rhsTypes++ droppedKinds :: [Kind]+ droppedKinds = map tvbKind . snd $ splitAt remainingLength famTvbs++ droppedNbs :: [NameBase]+ droppedNbs = map varTToNameBase dropped++ -- We need to be mindful of an old GHC bug which causes kind variables to appear in+ -- @instTysAndKinds@ (as the name suggests) if+ --+ -- (1) @PolyKinds@ is enabled+ -- (2) either GHC 7.6 or 7.8 is being used (for more info, see+ -- https://ghc.haskell.org/trac/ghc/ticket/9692).+ --+ -- Since Template Haskell doesn't seem to have a mechanism for detecting which+ -- language extensions are enabled, we do the next-best thing by counting+ -- the number of distinct kind variables in the data family declaration, and+ -- then dropping that number of entries from @instTysAndKinds@.+ instTypes :: [Type]+ instTypes =+# if __GLASGOW_HASKELL__ >= 710 || !(MIN_VERSION_template_haskell(2,8,0))+ instTysAndKinds+# else+ drop (Set.size . Set.unions $ map (distinctKindVars . tvbKind) famTvbs)+ instTysAndKinds+# endif++ lhsTvbs :: [TyVarBndr]+ lhsTvbs = map (uncurry replaceTyVarName)+ . filter (isTyVar . snd)+ . take remainingLength+ $ zip famTvbs rhsTypes++ -- In GHC 7.8, only the @Type@s up to the rightmost non-eta-reduced type variable+ -- in @instTypes@ are provided (as a result of this extremely annoying bug:+ -- https://ghc.haskell.org/trac/ghc/ticket/9692). This is pretty inconvenient,+ -- as it makes it impossible to come up with the correct Invariant(2)+ -- instances in some cases. For example, consider the following code:+ --+ -- @+ -- data family Foo a b c+ -- data instance Foo Int y z = Foo Int y z+ -- $(deriveInvariant2 'Foo)+ -- @+ --+ -- Due to the aformentioned bug, Template Haskell doesn't tell us the names of+ -- either of type variables in the data instance (@y@ and @z@). As a result, we+ -- won't know which fields of the 'Foo' constructor to apply the map functions,+ -- which will result in an incorrect instance. Urgh.+ --+ -- A workaround is to ensure that you use the exact same type variables, in the+ -- exact same order, in the data family declaration and any data or newtype+ -- instances:+ --+ -- @+ -- data family Foo a b c+ -- data instance Foo Int b c = Foo Int b c+ -- $(deriveInvariant2 'Foo)+ -- @+ --+ -- Thankfully, other versions of GHC don't seem to have this bug.+ rhsTypes :: [Type]+ rhsTypes =+# if __GLASGOW_HASKELL__ >= 708 && __GLASGOW_HASKELL__ < 710+ instTypes ++ map tvbToType+ (drop (length instTypes)+ famTvbs)+# else+ instTypes+# endif+#endif++-- | Given a TyVarBndr, apply an Invariant(2) constraint to it, depending+-- on its kind.+applyInvariantConstraint :: TyVarBndr -> Pred+applyInvariantConstraint (PlainTV _) = error "Cannot constrain type of kind *"+applyInvariantConstraint (KindedTV name kind) = applyClass className name+ where+ className :: Name+ className = invariantClassNameTable . toEnum $ numKindArrows kind++-- | Can a kind signature inhabit an Invariant constraint?+--+-- Invariant: Kind k1 -> k2+-- Invariant2: Kind k1 -> k2 -> k3+needsConstraint :: InvariantClass -> Kind -> Bool+needsConstraint iClass kind =+ fromEnum iClass >= nka+ && nka >= fromEnum Invariant+ && canRealizeKindStarChain kind+ where+ nka :: Int+ nka = numKindArrows kind++-------------------------------------------------------------------------------+-- Error messages+-------------------------------------------------------------------------------++-- | Either the given data type doesn't have enough type variables, or one of+-- the type variables to be eta-reduced cannot realize kind *.+derivingKindError :: InvariantClass -> Name -> a+derivingKindError iClass tyConName = error+ . showString "Cannot derive well-kinded instance of form ‘"+ . showString className+ . showChar ' '+ . showParen True+ ( showString (nameBase tyConName)+ . showString " ..."+ )+ . showString "‘\n\tClass "+ . showString className+ . showString " expects an argument of kind "+ . showString (pprint . createKindChain $ fromEnum iClass)+ $ ""+ where+ className :: String+ className = nameBase $ invariantClassNameTable iClass++-- | The data type has a DatatypeContext which mentions one of the eta-reduced+-- type variables.+datatypeContextError :: Name -> Type -> a+datatypeContextError dataName instanceType = error+ . showString "Can't make a derived instance of ‘"+ . showString (pprint instanceType)+ . showString "‘:\n\tData type ‘"+ . showString (nameBase dataName)+ . showString "‘ must not have a class context involving the last type argument(s)"+ $ ""++-- | The data type has an existential constraint which mentions one of the+-- eta-reduced type variables.+existentialContextError :: Name -> a+existentialContextError conName = error+ . showString "Constructor ‘"+ . showString (nameBase conName)+ . showString "‘ must be truly polymorphic in the last argument(s) of the data type"+ $ ""++-- | The data type mentions one of the n eta-reduced type variables in a place other+-- than the last nth positions of a data type in a constructor's field.+outOfPlaceTyVarError :: Name -> [NameBase] -> a+outOfPlaceTyVarError conName tyVarNames = error+ . showString "Constructor ‘"+ . showString (nameBase conName)+ . showString "‘ must use the type variable(s) "+ . showsPrec 0 tyVarNames+ . showString " only in the last argument(s) of a data type"+ $ ""++#if MIN_VERSION_template_haskell(2,7,0)+-- | One of the last type variables cannot be eta-reduced (see the canEtaReduce+-- function for the criteria it would have to meet).+etaReductionError :: Type -> a+etaReductionError instanceType = error $+ "Cannot eta-reduce to an instance of form \n\tinstance (...) => "+ ++ pprint instanceType+#else+-- | Template Haskell didn't list all of a data family's instances upon reification+-- until template-haskell-2.7.0.0, which is necessary for a derived Invariant instance+-- to work.+dataConIError :: a+dataConIError = error+ . showString "Cannot use a data constructor."+ . showString "\n\t(Note: if you are trying to derive Invariant for a type family,"+ . showString "\n\tuse GHC >= 7.4 instead.)"+ $ ""+#endif
+ src/Data/Functor/Invariant/TH/Internal.hs view
@@ -0,0 +1,437 @@+{-# LANGUAGE CPP #-}++{-|+Module: Data.Functor.Invariant.TH.Internal+Copyright: (C) 2012-2015 Nicolas Frisby, (C) 2015 Ryan Scott+License: BSD-style (see the file LICENSE)+Maintainer: Ryan Scott+Portability: Template Haskell++Template Haskell-related utilities.+-}+module Data.Functor.Invariant.TH.Internal where++import Data.Function (on)+import Data.List+import qualified Data.Map as Map (fromList, lookup)+import Data.Map (Map)+import Data.Maybe+import qualified Data.Set as Set+import Data.Set (Set)++import Language.Haskell.TH.Lib+import Language.Haskell.TH.Syntax++#ifndef CURRENT_PACKAGE_KEY+import Data.Version (showVersion)+import Paths_invariant (version)+#endif++-------------------------------------------------------------------------------+-- Expanding type synonyms+-------------------------------------------------------------------------------++-- | Expands all type synonyms in a type. Written by Dan Rosén in the+-- @genifunctors@ package (licensed under BSD3).+expandSyn :: Type -> Q Type+expandSyn (ForallT tvs ctx t) = fmap (ForallT tvs ctx) $ expandSyn t+expandSyn t@AppT{} = expandSynApp t []+expandSyn t@ConT{} = expandSynApp t []+expandSyn (SigT t _) = expandSyn t -- Ignore kind synonyms+expandSyn t = return t++expandSynApp :: Type -> [Type] -> Q Type+expandSynApp (AppT t1 t2) ts = do+ t2' <- expandSyn t2+ expandSynApp t1 (t2':ts)+expandSynApp (ConT n) ts | nameBase n == "[]" = return $ foldl' AppT ListT ts+expandSynApp t@(ConT n) ts = do+ info <- reify n+ case info of+ TyConI (TySynD _ tvs rhs) ->+ let (ts', ts'') = splitAt (length tvs) ts+ subs = mkSubst tvs ts'+ rhs' = subst subs rhs+ in expandSynApp rhs' ts''+ _ -> return $ foldl' AppT t ts+expandSynApp t ts = do+ t' <- expandSyn t+ return $ foldl' AppT t' ts++type Subst = Map Name Type++mkSubst :: [TyVarBndr] -> [Type] -> Subst+mkSubst vs ts =+ let vs' = map un vs+ un (PlainTV v) = v+ un (KindedTV v _) = v+ in Map.fromList $ zip vs' ts++subst :: Subst -> Type -> Type+subst subs (ForallT v c t) = ForallT v c $ subst subs t+subst subs t@(VarT n) = fromMaybe t $ Map.lookup n subs+subst subs (AppT t1 t2) = AppT (subst subs t1) (subst subs t2)+subst subs (SigT t k) = SigT (subst subs t) k+subst _ t = t++-------------------------------------------------------------------------------+-- Class-specific constants+-------------------------------------------------------------------------------++-- | A representation of which @Invariant@ is being used.+data InvariantClass = Invariant | Invariant2+ deriving (Eq, Ord)++instance Enum InvariantClass where+ fromEnum Invariant = 1+ fromEnum Invariant2 = 2++ toEnum 1 = Invariant+ toEnum 2 = Invariant2+ toEnum i = error $ "No Invariant class for number " ++ show i++invmapConstNameTable :: InvariantClass -> Name+invmapConstNameTable Invariant = invmapConstValName+invmapConstNameTable Invariant2 = invmap2ConstValName++invariantClassNameTable :: InvariantClass -> Name+invariantClassNameTable Invariant = invariantTypeName+invariantClassNameTable Invariant2 = invariant2TypeName++invmapNameTable :: InvariantClass -> Name+invmapNameTable Invariant = invmapValName+invmapNameTable Invariant2 = invmap2ValName++-- | A type-restricted version of 'const'. This constrains the map functions+-- that are autogenerated by Template Haskell to be the correct type, even+-- if they aren't actually used in an invmap(2) expression. This is useful+-- in makeInvmap(2), since a map function might have its type inferred as+-- @a@ instead of @a -> b@ (which is clearly wrong).+invmapConst :: f b -> (a -> b) -> (b -> a) -> f a -> f b+invmapConst = const . const . const+{-# INLINE invmapConst #-}++invmap2Const :: f c d+ -> (a -> c) -> (c -> a)+ -> (b -> d) -> (d -> b)+ -> f a b -> f c d+invmap2Const = const . const . const . const . const+{-# INLINE invmap2Const #-}++-------------------------------------------------------------------------------+-- NameBase+-------------------------------------------------------------------------------++-- | A wrapper around Name which only uses the 'nameBase' (not the entire Name)+-- to compare for equality. For example, if you had two Names a_123 and a_456,+-- they are not equal as Names, but they are equal as NameBases.+--+-- This is useful when inspecting type variables, since a type variable in an+-- instance context may have a distinct Name from a type variable within an+-- actual constructor declaration, but we'd want to treat them as the same+-- if they have the same 'nameBase' (since that's what the programmer uses to+-- begin with).+newtype NameBase = NameBase { getName :: Name }++getNameBase :: NameBase -> String+getNameBase = nameBase . getName++instance Eq NameBase where+ (==) = (==) `on` getNameBase++instance Ord NameBase where+ compare = compare `on` getNameBase++instance Show NameBase where+ showsPrec p = showsPrec p . getNameBase++-- | A NameBase paired with the name of its map functions. For example, when deriving+-- Invariant2, its list of TyVarInfos might look like [(a, 'covMap1, 'contraMap1),+-- (b, 'covMap2, 'contraMap2)].+type TyVarInfo = (NameBase, Name, Name)++-------------------------------------------------------------------------------+-- Assorted utilities+-------------------------------------------------------------------------------++fst3 :: (a, b, c) -> a+fst3 (a, _, _) = a++thd3 :: (a, b, c) -> c+thd3 (_, _, c) = c++-- Like 'lookup', but for lists of triples.+lookup2 :: Eq a => a -> [(a, b, c)] -> Maybe (b, c)+lookup2 _ [] = Nothing+lookup2 key ((x,y,z):xyzs)+ | key == x = Just (y, z)+ | otherwise = lookup2 key xyzs++-- | Extracts the name of a constructor.+constructorName :: Con -> Name+constructorName (NormalC name _ ) = name+constructorName (RecC name _ ) = name+constructorName (InfixC _ name _ ) = name+constructorName (ForallC _ _ con) = constructorName con++-- | Generate a list of fresh names with a common prefix, and numbered suffixes.+newNameList :: String -> Int -> Q [Name]+newNameList prefix n = mapM (newName . (prefix ++) . show) [1..n]++-- | Remove any occurrences of a forall-ed type variable from a list of @TyVarInfo@s.+removeForalled :: [TyVarBndr] -> [TyVarInfo] -> [TyVarInfo]+removeForalled tvbs = filter (not . foralled tvbs)+ where+ foralled :: [TyVarBndr] -> TyVarInfo -> Bool+ foralled tvbs' tvi = fst3 tvi `elem` map (NameBase . tvbName) tvbs'++-- | Extracts the name from a TyVarBndr.+tvbName :: TyVarBndr -> Name+tvbName (PlainTV name) = name+tvbName (KindedTV name _) = name++-- | Extracts the kind from a TyVarBndr.+tvbKind :: TyVarBndr -> Kind+tvbKind (PlainTV _) = starK+tvbKind (KindedTV _ k) = k++-- | Replace the Name of a TyVarBndr with one from a Type (if the Type has a Name).+replaceTyVarName :: TyVarBndr -> Type -> TyVarBndr+replaceTyVarName tvb (SigT t _) = replaceTyVarName tvb t+replaceTyVarName (PlainTV _) (VarT n) = PlainTV n+replaceTyVarName (KindedTV _ k) (VarT n) = KindedTV n k+replaceTyVarName tvb _ = tvb++-- | Applies a typeclass constraint to a type.+applyClass :: Name -> Name -> Pred+#if MIN_VERSION_template_haskell(2,10,0)+applyClass con t = AppT (ConT con) (VarT t)+#else+applyClass con t = ClassP con [VarT t]+#endif++-- | Checks to see if the last types in a data family instance can be safely eta-+-- reduced (i.e., dropped), given the other types. This checks for three conditions:+--+-- (1) All of the dropped types are type variables+-- (2) All of the dropped types are distinct+-- (3) None of the remaining types mention any of the dropped types+canEtaReduce :: [Type] -> [Type] -> Bool+canEtaReduce remaining dropped =+ all isTyVar dropped+ && allDistinct nbs -- Make sure not to pass something of type [Type], since Type+ -- didn't have an Ord instance until template-haskell-2.10.0.0+ && not (any (`mentionsNameBase` nbs) remaining)+ where+ nbs :: [NameBase]+ nbs = map varTToNameBase dropped++-- | Extract the Name from a type variable.+varTToName :: Type -> Name+varTToName (VarT n) = n+varTToName (SigT t _) = varTToName t+varTToName _ = error "Not a type variable!"++-- | Extract the NameBase from a type variable.+varTToNameBase :: Type -> NameBase+varTToNameBase = NameBase . varTToName++-- | Peel off a kind signature from a Type (if it has one).+unSigT :: Type -> Type+unSigT (SigT t _) = t+unSigT t = t++-- | Is the given type a variable?+isTyVar :: Type -> Bool+isTyVar (VarT _) = True+isTyVar (SigT t _) = isTyVar t+isTyVar _ = False++-- | Is the given type a type family constructor (and not a data family constructor)?+isTyFamily :: Type -> Q Bool+isTyFamily (ConT n) = do+ info <- reify n+ return $ case info of+#if MIN_VERSION_template_haskell(2,7,0)+ FamilyI (FamilyD TypeFam _ _ _) _ -> True+#else+ TyConI (FamilyD TypeFam _ _ _) -> True+#endif+ _ -> False+isTyFamily _ = return False++-- | Are all of the items in a list (which have an ordering) distinct?+--+-- This uses Set (as opposed to nub) for better asymptotic time complexity.+allDistinct :: Ord a => [a] -> Bool+allDistinct = allDistinct' Set.empty+ where+ allDistinct' :: Ord a => Set a -> [a] -> Bool+ allDistinct' uniqs (x:xs)+ | x `Set.member` uniqs = False+ | otherwise = allDistinct' (Set.insert x uniqs) xs+ allDistinct' _ _ = True++-- | Does the given type mention any of the NameBases in the list?+mentionsNameBase :: Type -> [NameBase] -> Bool+mentionsNameBase = go Set.empty+ where+ go :: Set NameBase -> Type -> [NameBase] -> Bool+ go foralls (ForallT tvbs _ t) nbs =+ go (foralls `Set.union` Set.fromList (map (NameBase . tvbName) tvbs)) t nbs+ go foralls (AppT t1 t2) nbs = go foralls t1 nbs || go foralls t2 nbs+ go foralls (SigT t _) nbs = go foralls t nbs+ go foralls (VarT n) nbs = varNb `elem` nbs && not (varNb `Set.member` foralls)+ where+ varNb = NameBase n+ go _ _ _ = False++-- | Does an instance predicate mention any of the NameBases in the list?+predMentionsNameBase :: Pred -> [NameBase] -> Bool+#if MIN_VERSION_template_haskell(2,10,0)+predMentionsNameBase = mentionsNameBase+#else+predMentionsNameBase (ClassP _ tys) nbs = any (`mentionsNameBase` nbs) tys+predMentionsNameBase (EqualP t1 t2) nbs = mentionsNameBase t1 nbs || mentionsNameBase t2 nbs+#endif++-- | The number of arrows that compose the spine of a kind signature+-- (e.g., (* -> *) -> k -> * has two arrows on its spine).+numKindArrows :: Kind -> Int+numKindArrows k = length (uncurryKind k) - 1++-- | Construct a type via curried application.+applyTy :: Type -> [Type] -> Type+applyTy = foldl' AppT++-- | Fully applies a type constructor to its type variables.+applyTyCon :: Name -> [Type] -> Type+applyTyCon = applyTy . ConT++-- | Split an applied type into its individual components. For example, this:+--+-- @+-- Either Int Char+-- @+--+-- would split to this:+--+-- @+-- [Either, Int, Char]+-- @+unapplyTy :: Type -> [Type]+unapplyTy = reverse . go+ where+ go :: Type -> [Type]+ go (AppT t1 t2) = t2:go t1+ go (SigT t _) = go t+ go t = [t]++-- | Split a type signature by the arrows on its spine. For example, this:+--+-- @+-- (Int -> String) -> Char -> ()+-- @+--+-- would split to this:+--+-- @+-- [Int -> String, Char, ()]+-- @+uncurryTy :: Type -> [Type]+uncurryTy (AppT (AppT ArrowT t1) t2) = t1:uncurryTy t2+uncurryTy (SigT t _) = uncurryTy t+uncurryTy t = [t]++-- | Like uncurryType, except on a kind level.+uncurryKind :: Kind -> [Kind]+#if MIN_VERSION_template_haskell(2,8,0)+uncurryKind = uncurryTy+#else+uncurryKind (ArrowK k1 k2) = k1:uncurryKind k2+uncurryKind k = [k]+#endif++wellKinded :: [Kind] -> Bool+wellKinded = all canRealizeKindStar++-- | Of form k1 -> k2 -> ... -> kn, where k is either a single kind variable or *.+canRealizeKindStarChain :: Kind -> Bool+canRealizeKindStarChain = all canRealizeKindStar . uncurryKind++canRealizeKindStar :: Kind -> Bool+canRealizeKindStar k = case uncurryKind k of+ [k'] -> case k' of+#if MIN_VERSION_template_haskell(2,8,0)+ StarT -> True+ (VarT _) -> True -- Kind k can be instantiated with *+#else+ StarK -> True+#endif+ _ -> False+ _ -> False++createKindChain :: Int -> Kind+createKindChain = go starK+ where+ go :: Kind -> Int -> Kind+ go k 0 = k+#if MIN_VERSION_template_haskell(2,8,0)+ go k n = n `seq` go (AppT (AppT ArrowT StarT) k) (n - 1)+#else+ go k n = n `seq` go (ArrowK StarK k) (n - 1)+#endif++distinctKindVars :: Kind -> Set Name+#if MIN_VERSION_template_haskell(2,8,0)+distinctKindVars (AppT k1 k2) = distinctKindVars k1 `Set.union` distinctKindVars k2+distinctKindVars (SigT k _) = distinctKindVars k+distinctKindVars (VarT k) = Set.singleton k+#endif+distinctKindVars _ = Set.empty++tvbToType :: TyVarBndr -> Type+tvbToType (PlainTV n) = VarT n+tvbToType (KindedTV n k) = SigT (VarT n) k++-------------------------------------------------------------------------------+-- Manually quoted names+-------------------------------------------------------------------------------++-- By manually generating these names we avoid needing to use the+-- TemplateHaskell language extension when compiling the invariant library.+-- This allows the library to be used in stage1 cross-compilers.++invariantPackageKey :: String+#ifdef CURRENT_PACKAGE_KEY+invariantPackageKey = CURRENT_PACKAGE_KEY+#else+invariantPackageKey = "invariant-" ++ showVersion version+#endif++mkInvariantName_tc :: String -> String -> Name+mkInvariantName_tc = mkNameG_tc invariantPackageKey++mkInvariantName_v :: String -> String -> Name+mkInvariantName_v = mkNameG_v invariantPackageKey++invariantTypeName :: Name+invariantTypeName = mkInvariantName_tc "Data.Functor.Invariant" "Invariant"++invariant2TypeName :: Name+invariant2TypeName = mkInvariantName_tc "Data.Functor.Invariant" "Invariant2"++invmapValName :: Name+invmapValName = mkInvariantName_v "Data.Functor.Invariant" "invmap"++invmap2ValName :: Name+invmap2ValName = mkInvariantName_v "Data.Functor.Invariant" "invmap2"++invmapConstValName :: Name+invmapConstValName = mkInvariantName_v "Data.Functor.Invariant.TH.Internal" "invmapConst"++invmap2ConstValName :: Name+invmap2ConstValName = mkInvariantName_v "Data.Functor.Invariant.TH.Internal" "invmap2Const"++errorValName :: Name+errorValName = mkNameG_v "base" "GHC.Err" "error"
+ test/InvariantSpec.hs view
@@ -0,0 +1,60 @@+module InvariantSpec (main, spec) where++import Data.Functor.Invariant+import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck+import Test.QuickCheck.Function++main :: IO ()+main = hspec spec++data Proxy a = Proxy++-----++-- These test could probably be simplified by appealing to parametricity.+spec :: Spec+spec = do+ describe "Invariant" . prop "satisfies the composition law" $+ composition1 (Proxy :: Proxy Integer)+ (Proxy :: Proxy Bool)+ (Proxy :: Proxy [Bool])+ describe "Invariant2" . prop "satisfies the composition law" $+ composition2 (Proxy :: Proxy Integer)+ (Proxy :: Proxy Bool)+ (Proxy :: Proxy Integer)+ (Proxy :: Proxy Bool)+ (Proxy :: Proxy (Bool,Bool))++-----++composition1+ :: (Eq (f c), Show (f c), Invariant f)+ => proxy b -> proxy c -> proxy (f a)+ -> Fun b c -> Fun c b+ -> Fun a b -> Fun b a+ -> f a+ -> Property+composition1+ _ _ _+ (Fun _ f) (Fun _ f')+ (Fun _ g) (Fun _ g')+ x =+ (invmap f f' . invmap g g') x+ === invmap (f . g) (g' . f') x++composition2+ :: (Eq (f c1 c2), Show (f c1 c2), Invariant2 f)+ => proxy b1 -> proxy c1 -> proxy b2 -> proxy c2 -> proxy (f a1 a2)+ -> Fun b1 c1 -> Fun c1 b1 -> Fun b2 c2 -> Fun c2 b2+ -> Fun a1 b1 -> Fun b1 a1 -> Fun a2 b2 -> Fun b2 a2+ -> f a1 a2+ -> Property+composition2+ _ _ _ _ _+ (Fun _ f1) (Fun _ f1') (Fun _ f2) (Fun _ f2')+ (Fun _ g1) (Fun _ g1') (Fun _ g2) (Fun _ g2')+ x =+ (invmap2 f1 f1' f2 f2' . invmap2 g1 g1' g2 g2') x+ === invmap2 (f1 . g1) (g1' . f1') (f2 . g2) (g2' . f2') x
+ test/THSpec.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+{-# OPTIONS_GHC -fno-warn-unused-matches #-}+module THSpec (main, spec) where++import Data.Functor.Invariant+import Data.Functor.Invariant.TH++import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck (Arbitrary)++-------------------------------------------------------------------------------++-- Adapted from the test cases from+-- https://ghc.haskell.org/trac/ghc/attachment/ticket/2953/deriving-functor-tests.patch++data Strange a b c+ = T1 a b c+ | T2 [a] [b] [c] -- lists+ | T3 [[a]] [[b]] [[c]] -- nested lists+ | T4 (c,(b,b),(c,c)) -- tuples+ | T5 ([c],Strange a b c) -- tycons+ | T6 (b -> c) -- function types+ | T7 (b -> (c,a)) -- functions and tuples+ | T8 ((c -> b) -> a) -- continuation++data NotPrimitivelyRecursive a b+ = S1 (NotPrimitivelyRecursive (a,a) (b, a))+ | S2 a+ | S3 b++newtype Compose f g a b = Compose (f (g a b))+ deriving (Arbitrary, Eq, Show)++data ComplexConstraint f a b = ComplexContraint (f Int Int (f Bool Bool a,a,b))++data Universal a+ = Universal (forall b. (b,[a]))+ | Universal2 (forall f. Invariant f => (f a))+ | Universal3 (forall a. a -> Int) -- reuse a+ | NotReallyUniversal (forall b. a)++data Existential b+ = forall a. ExistentialList [a]+ | forall f. Invariant f => ExistentialFunctor (f b)+ | forall b. SneakyUseSameName (b -> Bool)++type IntFun a b = b -> a+data IntFunD a b = IntFunD (IntFun a b)++-------------------------------------------------------------------------------++$(deriveInvariant ''Strange)+$(deriveInvariant2 ''Strange)++$(deriveInvariant ''NotPrimitivelyRecursive)+$(deriveInvariant2 ''NotPrimitivelyRecursive)++instance (Invariant f, Invariant (g a)) =>+ Invariant (Compose f g a) where+ invmap = $(makeInvmap ''Compose)+$(deriveInvariant2 ''Compose)++instance Invariant (f Int Int) =>+ Invariant (ComplexConstraint f a) where+ invmap = $(makeInvmap ''ComplexConstraint)+instance (Invariant2 (f Bool), Invariant2 (f Int)) =>+ Invariant2 (ComplexConstraint f) where+ invmap2 = $(makeInvmap2 ''ComplexConstraint)++$(deriveInvariant ''Universal)++$(deriveInvariant ''Existential)++$(deriveInvariant ''IntFunD)+$(deriveInvariant2 ''IntFunD)++-------------------------------------------------------------------------------++-- | Verifies that @invmap id id = id@ (the other 'invmap' law follows+-- as a free theorem:+-- https://www.fpcomplete.com/user/edwardk/snippets/fmap).+prop_invmapLaws :: (Eq (f a), Invariant f) => f a -> Bool+prop_invmapLaws x = invmap id id x == x++-- | Verifies that @invmap2 id id id id = id@.+prop_invmap2Laws :: (Eq (f a b), Invariant2 f) => f a b -> Bool+prop_invmap2Laws x = invmap2 id id id id x == x++-------------------------------------------------------------------------------++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "Compose Maybe Either Int Int" $ do+ prop "satisfies the invmap laws" (prop_invmapLaws :: Compose Maybe Either Int Int -> Bool)+ prop "satisfies the invmap2 laws" (prop_invmap2Laws :: Compose Maybe Either Int Int -> Bool)