packages feed

th-abstraction 0.1.2.1 → 0.1.3.0

raw patch · 4 files changed

+185/−46 lines, 4 filesdep ~template-haskellPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: template-haskell

API changes (from Hackage documentation)

+ Language.Haskell.TH.Datatype: asClassPred :: Pred -> Maybe (Name, [Type])
+ Language.Haskell.TH.Datatype: asEqualPred :: Pred -> Maybe (Type, Type)
+ Language.Haskell.TH.Datatype: resolveInfixT :: Type -> Q Type
+ Language.Haskell.TH.Datatype: showFixity :: Fixity -> String
+ Language.Haskell.TH.Datatype: showFixityDirection :: FixityDirection -> String
- Language.Haskell.TH.Datatype: normalizeCon :: Name -> [Name] -> Con -> Q [ConstructorInfo]
+ Language.Haskell.TH.Datatype: normalizeCon :: Name -> [Type] -> Con -> Q [ConstructorInfo]

Files

ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for th-abstraction +## 0.1.3.0 -- 2016-05-27++* Added `resolveInfixT` which uses reified fixity information to resolve `UInfixT`+* Added `asEqualPred` and `asClassPred`+* Fixed data-instance GADTs+ ## 0.1.2.1  -- 2017-05-21  * Add eta reduction fixes to GHC 7.6
src/Language/Haskell/TH/Datatype.hs view
@@ -64,16 +64,23 @@   -- * 'Pred' functions   , equalPred   , classPred+  , asEqualPred+  , asClassPred    -- * Backward compatible data definitions   , dataDCompat   , arrowKCompat -  -- * Convenience functions+  -- * Type simplification   , resolveTypeSynonyms+  , resolveInfixT++  -- * Convenience functions   , unifyTypes   , tvName   , datatypeType+  , showFixity+  , showFixityDirection   ) where  import           Data.Data (Typeable, Data)@@ -81,9 +88,11 @@ import           Data.List (find, union, (\\)) import           Data.Map (Map) import qualified Data.Map as Map+import           Data.Maybe (fromMaybe) import           Control.Monad (foldM) import           GHC.Generics (Generic) import           Language.Haskell.TH+import           Language.Haskell.TH.Datatype.Internal import           Language.Haskell.TH.Lib (arrowK) -- needed for th-2.4  #if !MIN_VERSION_base(4,8,0)@@ -128,10 +137,11 @@   -- | Construct a Type using the datatype's type constructor and type--- parameters.+-- parameters. Kind signatures are removed. datatypeType :: DatatypeInfo -> Type datatypeType di   = foldl AppT (ConT (datatypeName di))+  $ map stripSigT   $ datatypeVars di  @@ -245,6 +255,12 @@     PlainTV  t   -> VarT t  +-- | Remove the outermost 'SigT'.+stripSigT :: Type -> Type+stripSigT (SigT t _) = t+stripSigT t          = t++ normalizeDec' ::   Cxt             {- ^ Datatype context    -} ->   Name            {- ^ Type constructor    -} ->@@ -253,8 +269,7 @@   DatatypeVariant {- ^ Extra information   -} ->   Q DatatypeInfo normalizeDec' context name params cons variant =-  do let vs = freeVariables params-     cons' <- concat <$> traverse (normalizeCon name vs) cons+  do cons' <- concat <$> traverse (normalizeCon name params) cons      pure DatatypeInfo        { datatypeContext = context        , datatypeName    = name@@ -268,10 +283,10 @@ -- 'Dec'. normalizeCon ::   Name   {- ^ Type constructor -} ->-  [Name] {- ^ Type parameters  -} ->+  [Type] {- ^ Type parameters  -} ->   Con    {- ^ Constructor      -} ->   Q [ConstructorInfo]-normalizeCon typename vars = go [] []+normalizeCon typename params = go [] []   where     go tyvars context c =       case c of@@ -293,12 +308,12 @@           let fns = takeFieldNames xs in           gadtCase ns innerType (takeFieldTypes xs) (RecordConstructor fns)       where-        gadtCase = normalizeGadtC typename vars tyvars context+        gadtCase = normalizeGadtC typename params tyvars context   normalizeGadtC ::   Name               {- ^ Type constructor             -} ->-  [Name]             {- ^ Type parameters              -} ->+  [Type]             {- ^ Type parameters              -} ->   [TyVarBndr]        {- ^ Constructor parameters       -} ->   Cxt                {- ^ Constructor context          -} ->   [Name]             {- ^ Constructor names            -} ->@@ -306,12 +321,12 @@   [Type]             {- ^ Constructor field types      -} ->   ConstructorVariant {- ^ Constructor variant          -} ->   Q [ConstructorInfo]-normalizeGadtC typename vars tyvars context names innerType fields variant =+normalizeGadtC typename params tyvars context names innerType fields variant =   do innerType' <- resolveTypeSynonyms innerType      case decomposeType innerType' of        ConT innerTyCon :| ts | typename == innerTyCon -> -         let (substName, context1) = mergeArguments vars ts+         let (substName, context1) = mergeArguments params ts              subst   = VarT <$> substName              tyvars' = [ tv | tv <- tyvars, Map.notMember (tvName tv) subst ] @@ -322,14 +337,24 @@         _ -> fail "normalizeGadtC: Expected type constructor application" -mergeArguments :: [Name] -> [Type] -> (Map Name Name, Cxt)+mergeArguments ::+  [Type] {- ^ outer parameters                    -} ->+  [Type] {- ^ inner parameters (specializations ) -} ->+  (Map Name Name, Cxt) mergeArguments ns ts = foldr aux (Map.empty, []) (zip ns ts)   where-    aux (n,p) (subst, context) =+    aux (SigT x _, y) sc = aux (x,y) sc -- learn about kinds??+    aux (x, SigT y _) sc = aux (x,y) sc++    aux (f `AppT` x, g `AppT` y) sc =+      aux (x,y) (aux (f,g) sc)++    aux (VarT n,p) (subst, context) =       case p of         VarT m | Map.notMember m subst -> (Map.insert m n subst, context)         _ -> (subst, EqualityT `AppT` VarT n `AppT` p : context) +    aux _ sc = sc #endif  -- | Expand all of the type synonyms in a type.@@ -337,38 +362,119 @@ resolveTypeSynonyms t =   let f :| xs = decomposeType t -      notTypeSynCase = foldl AppT f <$> traverse resolveTypeSynonyms xs+      notTypeSynCase = foldl AppT f <$> traverse resolveTypeSynonyms xs in -  in case f of-       ConT n ->-         do info <- reify n-            case info of-              TyConI (TySynD _ synvars def) ->-                let argNames    = map tvName synvars-                    (args,rest) = splitAt (length argNames) xs-                    subst       = Map.fromList (zip argNames args)-                    t'          = foldl AppT (applySubstitution subst def) rest-                in resolveTypeSynonyms t'+  case f of+    ConT n ->+      do info <- reify n+         case info of+           TyConI (TySynD _ synvars def) ->+             let argNames    = map tvName synvars+                 (args,rest) = splitAt (length argNames) xs+                 subst       = Map.fromList (zip argNames args)+                 t'          = foldl AppT (applySubstitution subst def) rest+             in resolveTypeSynonyms t' -              _ -> notTypeSynCase-       _ -> notTypeSynCase+           _ -> notTypeSynCase+    _ -> notTypeSynCase  -- | Decompose a type into a list of it's outermost applications. This process -- forgets about infix application and explicit parentheses. --+-- This operation should be used after all 'UInfixT' cases have been resolved+-- by 'resolveFixities' if the argument is being user generated.+-- -- > t ~= foldl1 AppT (decomposeType t) decomposeType :: Type -> NonEmpty Type-decomposeType = reverseNonEmpty . go+decomposeType = go []   where-    go (AppT f x     ) = x <| go f+    go args (AppT f x) = go (x:args) f+    go args t          = t :| args++-- 'NonEmpty' didn't move into base until recently. Reimplementing it locally+-- saves dependencies for supporting older GHCs+data NonEmpty a = a :| [a]+++-- | Resolve any infix type application in a type using the fixities that+-- are currently available. Starting in `template-haskell-2.11` types could+-- contain unresolved infix applications.+resolveInfixT :: Type -> Q Type+ #if MIN_VERSION_template_haskell(2,11,0)-    go (InfixT  l f r) = ConT f :| [l,r]-    go (UInfixT l f r) = ConT f :| [l,r]-    go (ParensT t    ) = decomposeType t+resolveInfixT (ForallT vs cx t) = forallT vs (traverse resolveInfixT cx) (resolveInfixT t)+resolveInfixT (f `AppT` x)      = resolveInfixT f `appT` resolveInfixT x+resolveInfixT (ParensT t)       = resolveInfixT t+resolveInfixT (InfixT l o r)    = conT o `appT` resolveInfixT l `appT` resolveInfixT r+resolveInfixT (SigT t k)        = SigT <$> resolveInfixT t <*> resolveInfixT k+resolveInfixT t@UInfixT{}       = resolveInfixT =<< resolveInfixT1 (gatherUInfixT t)+resolveInfixT t                 = pure t++gatherUInfixT :: Type -> InfixList+gatherUInfixT (UInfixT l o r) = ilAppend (gatherUInfixT l) o (gatherUInfixT r)+gatherUInfixT t = ILNil t++-- This can fail due to incompatible fixities+resolveInfixT1 :: InfixList -> TypeQ+resolveInfixT1 = go []+  where+    go :: [(Type,Name,Fixity)] -> InfixList -> TypeQ+    go ts (ILNil u) = pure (foldl (\acc (l,o,_) -> ConT o `AppT` l `AppT` acc) u ts)+    go ts (ILCons l o r) =+      do ofx <- fromMaybe defaultFixity <$> reifyFixity o+         let push = go ((l,o,ofx):ts) r+         case ts of+           (l1,o1,o1fx):ts' ->+             case compareFixity o1fx ofx of+               Just True  -> go ((ConT o1 `AppT` l1 `AppT` l, o, ofx):ts') r+               Just False -> push+               Nothing    -> fail (precedenceError o1 o1fx o ofx)+           _ -> push++    compareFixity :: Fixity -> Fixity -> Maybe Bool+    compareFixity (Fixity n1 InfixL) (Fixity n2 InfixL) = Just (n1 >= n2)+    compareFixity (Fixity n1 InfixR) (Fixity n2 InfixR) = Just (n1 >  n2)+    compareFixity (Fixity n1 _     ) (Fixity n2 _     ) =+      case compare n1 n2 of+        GT -> Just True+        LT -> Just False+        EQ -> Nothing++    precedenceError :: Name -> Fixity -> Name -> Fixity -> String+    precedenceError o1 ofx1 o2 ofx2 =+      "Precedence parsing error: cannot mix ‘" +++      nameBase o1 ++ "’ [" ++ showFixity ofx1 ++ "] and ‘" +++      nameBase o2 ++ "’ [" ++ showFixity ofx2 +++      "] in the same infix type expression"++data InfixList = ILCons Type Name InfixList | ILNil Type++ilAppend :: InfixList -> Name -> InfixList -> InfixList+ilAppend (ILNil l)         o r = ILCons l o r+ilAppend (ILCons l1 o1 r1) o r = ILCons l1 o1 (ilAppend r1 o r)++#else+-- older template-haskell packages don't have UInfixT+resolveInfixT = pure #endif-    go t               = t :| []  +-- | Render a 'Fixity' as it would appear in Haskell source.+--+-- Example: @infixl 5@+showFixity :: Fixity -> String+showFixity (Fixity n d) = showFixityDirection d ++ " " ++ show n+++-- | Render a 'FixityDirection' like it would appear in Haskell source.+--+-- Examples: @infixl@ @infixr@ @infix@+showFixityDirection :: FixityDirection -> String+showFixityDirection InfixL = "infixl"+showFixityDirection InfixR = "infixr"+showFixityDirection InfixN = "infix"++ -- | Extract the type variable name from a 'TyVarBndr' ignoring the -- kind signature if one exists. tvName :: TyVarBndr -> Name@@ -388,7 +494,6 @@ -- This code is careful to ensure that the order of the variables quantified -- is determined by their order of appearance in the type signature. (In -- contrast with being dependent upon the Ord instance for 'Name')--- quantifyType :: Type -> Type quantifyType t   | null vs   = t@@ -543,19 +648,30 @@   ClassP #endif ------------------------------------------------------------------------- --- 'NonEmpty' didn't move into base until recently. Reimplementing it locally--- saves dependencies for supporting older GHCs--data NonEmpty a = a :| [a]--(<|) :: a -> NonEmpty a -> NonEmpty a-x <| (y :| ys) = x :| (y : ys)+-- | Match a 'Pred' representing an equality constraint. Returns+-- arguments to the equality constraint if successful.+asEqualPred :: Pred -> Maybe (Type,Type)+#if MIN_VERSION_template_haskell(2,10,0)+asEqualPred (EqualityT `AppT` x `AppT` y)                    = Just (x,y)+asEqualPred (ConT eq   `AppT` x `AppT` y) | eq == eqTypeName = Just (x,y)+#else+asEqualPred (EqualP            x        y)                   = Just (x,y)+#endif+asEqualPred _                                                = Nothing -reverseNonEmpty :: NonEmpty a -> NonEmpty a-reverseNonEmpty (x :| xs) = y :| ys-  where y:ys = reverse (x:xs)+-- | Match a 'Pred' representing a class constraint.+-- Returns the classname and parameters if successful.+asClassPred :: Pred -> Maybe (Name, [Type])+#if MIN_VERSION_template_haskell(2,10,0)+asClassPred t =+  case decomposeType t of+    ConT f :| xs | f /= eqTypeName -> Just (f,xs)+    _                              -> Nothing+#else+asClassPred (ClassP f xs) = Just (f,xs)+asClassPred _             = Nothing+#endif  ------------------------------------------------------------------------ 
+ src/Language/Haskell/TH/Datatype/Internal.hs view
@@ -0,0 +1,16 @@+{-|+Module      : Language.Haskell.TH.Datatype.Internal+Description : Backwards-compatible interface to reified information about datatypes.+Copyright   : Eric Mertens 2017+License     : ISC+Maintainer  : emertens@gmail.com++Internal Template Haskell 'Name's.++-}+module Language.Haskell.TH.Datatype.Internal where++import Language.Haskell.TH.Syntax++eqTypeName :: Name+eqTypeName = mkNameG_tc "base" "Data.Type.Equality" "~"
th-abstraction.cabal view
@@ -1,5 +1,5 @@ name:                th-abstraction-version:             0.1.2.1+version:             0.1.3.0 synopsis:            Nicer interface for reified information about data types description:         This package normalizes variations in the interface for                      inspecting datatype information via Template Haskell@@ -17,7 +17,7 @@ build-type:          Simple extra-source-files:  ChangeLog.md cabal-version:       >=1.10-tested-with:         GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.4.2+tested-with:         GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2  source-repository head   type: git@@ -25,9 +25,10 @@  library   exposed-modules:     Language.Haskell.TH.Datatype+  other-modules:       Language.Haskell.TH.Datatype.Internal   build-depends:       base             >=4.5   && <5,                        ghc-prim,-                       template-haskell >=2.4   && <2.13,+                       template-haskell >=2.7   && <2.13,                        containers       >=0.4   && <0.6   hs-source-dirs:      src   default-language:    Haskell2010