packages feed

th-expand-syns 0.1.1.0 → 0.2.0.0

raw patch · 3 files changed

+124/−43 lines, 3 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Language.Haskell.TH.ExpandSyns: substInCon :: (Name, Type) -> Con -> Con
- Language.Haskell.TH.ExpandSyns: substInType :: (Name, Type) -> Type -> Type
+ Language.Haskell.TH.ExpandSyns: class SubstTypeVariable a
+ Language.Haskell.TH.ExpandSyns: instance HasForallConstruct Con
+ Language.Haskell.TH.ExpandSyns: instance HasForallConstruct Type
+ Language.Haskell.TH.ExpandSyns: instance SubstTypeVariable Con
+ Language.Haskell.TH.ExpandSyns: instance SubstTypeVariable Pred
+ Language.Haskell.TH.ExpandSyns: instance SubstTypeVariable Type
+ Language.Haskell.TH.ExpandSyns: subst :: SubstTypeVariable a => (Name, Type) -> a -> a

Files

Language/Haskell/TH/ExpandSyns.hs view
@@ -4,7 +4,7 @@ module Language.Haskell.TH.ExpandSyns(-- * Expand synonyms                                       expandSyns                                       -- * Misc utilities-                                     ,substInType, substInCon+                                     ,SubstTypeVariable(..)                                      ,evades,evade) where      import Language.Haskell.TH hiding(cxt)@@ -16,7 +16,7 @@ packagename = "th-expand-syns"          --- Compatibility layer for TH 2.4 vs. 2.3+-- Compatibility layer for TH >=2.4 vs. 2.3 tyVarBndrGetName :: TyVarBndr -> Name mapPred :: (Type -> Type) -> Pred -> Pred bindPred :: (Type -> Q Type) -> Pred -> Q Pred@@ -45,8 +45,6 @@                         #endif -substInPred :: (Name, Type) -> Pred -> Pred-substInPred s = mapPred (substInType s)   (<$>) :: (Functor f) => (a -> b) -> f a -> f b@@ -60,19 +58,35 @@ nameIsSyn n = do   i <- reify n   case i of-    TyConI d -> return (decIsSyn d)+    TyConI d -> decIsSyn d     ClassI {} -> return Nothing     PrimTyConI {} -> return Nothing-    _ -> fail (packagename ++ ": nameIsSyn: unexpected Info: "++show(n,i))+    _ -> do +            warn ("Don't know how to interpret the result of reify "++show n++" (= "++show i++"). I will assume that "++show n++" is not a type synonym.")+            return Nothing+         -decIsSyn :: Dec -> Maybe SynInfo-decIsSyn (ClassD {}) = Nothing-decIsSyn (DataD {}) = Nothing-decIsSyn (NewtypeD {}) = Nothing-decIsSyn (TySynD _ vars t) = Just (tyVarBndrGetName <$> vars,t)-decIsSyn x = error (packagename ++ ": decIsSyn: unexpected Dec: "++show x) --- | Expands type synonyms...+warn ::  String -> Q ()+warn msg = report False (packagename ++": "++"WARNING: "++msg)++-- | Handles only declaration constructs that can be returned by 'reify'ing a type name.+decIsSyn :: Dec -> Q (Maybe SynInfo)+decIsSyn (ClassD {}) = return Nothing+decIsSyn (DataD {}) = return Nothing+decIsSyn (NewtypeD {}) = return Nothing+decIsSyn (TySynD _ vars t) = return (Just (tyVarBndrGetName <$> vars,t))+#if __GLASGOW_HASKELL__ >= 611+decIsSyn (FamilyD _ name _ _) = do+    warn ("Type families (data families, newtype families, associated types and type synonym families) are currently not supported (they won't be expanded). Name of unsupported family: "++show name) +    return Nothing+    +#endif+decIsSyn x = do+    warn ("Unrecognized declaration construct: "++ show x++". I will assume that it's not a type synonym declaration.")+    return Nothing++-- | Expands all type synonyms in the given type. Type families currently won't be expanded (but will be passed through). expandSyns :: Type -> Q Type expandSyns =      (\t ->@@ -113,7 +127,7 @@                   else                        let                           substs = zip vars acc-                          expanded = foldr substInType body substs+                          expanded = foldr subst body substs                       in                         go (drop (length vars) acc) expanded                         @@ -125,9 +139,14 @@             return (acc',SigT t' kind) #endif --- | Capture-free substitution-substInType :: (Name, Type) -> Type -> Type-substInType (v, t) = go+class SubstTypeVariable a where+    -- | Capture-free substitution+    subst :: (Name, Type) -> a -> a++++instance SubstTypeVariable Type where+  subst (v, t) = go     where       go (AppT x y) = AppT (go x) (go y)       go s@(ConT _) = s@@ -136,7 +155,7 @@       go ArrowT = ArrowT       go ListT = ListT       go (ForallT vars cxt body) = -          commonForallCase (v,t) ForallT substInType (vars,cxt,body)+          commonForallCase (v,t) (vars,cxt,body)                                go s@(TupleT _) = s                         @@ -157,10 +176,24 @@ --                    (v "x" `AppT` v "y"))                          -+#if __GLASGOW_HASKELL__ >= 611+instance SubstTypeVariable Pred where+    subst s = mapPred (subst s)+#endif           -- | Make a name (based on the first arg) that's distinct from every name in the second arg+--+-- Example why this is necessary:+--+-- > type E x = forall y. Either x y+-- >+-- > ... expandSyns [t| forall y. E y |]+--+-- The example as given may actually work correctly without any special capture-avoidance depending+-- on how GHC handles the @y@s, but in any case, the input type to expandSyns may be an explicit+-- AST using 'mkName' to ensure a collision.+-- evade :: Data d => Name -> d -> Name evade n t =      let@@ -186,29 +219,41 @@ --             in --               evade v (AppT (VarT v) (VarT (mkName "fx"))) -substInCon :: (Name, Type) -> Con -> Con-substInCon (v,t) = go+instance SubstTypeVariable Con where+  subst (v,t) = go     where-      st = substInType (v,t)+      st = subst (v,t)        go (NormalC n ts) = NormalC n [(x, st y) | (x,y) <- ts]       go (RecC n ts) = RecC n [(x, y, st z) | (x,y,z) <- ts]       go (InfixC (y1,t1) op (y2,t2)) = InfixC (y1,st t1) op (y2,st t2)       go (ForallC vars cxt body) = -          commonForallCase (v,t) ForallC substInCon (vars,cxt,body)+          commonForallCase (v,t) (vars,cxt,body)   +class HasForallConstruct a where+    mkForall :: [TyVarBndr] -> Cxt -> a -> a +instance HasForallConstruct Type where+    mkForall = ForallT +instance HasForallConstruct Con where+    mkForall = ForallC -commonForallCase :: (Name,Type) -                 -> ([TyVarBndr] -> Cxt -> a -> a) -                 -> ((Name,Type) -> a -> a)+++commonForallCase :: (SubstTypeVariable a, HasForallConstruct a) => ++                    (Name,Type)                   -> ([TyVarBndr],Cxt,a)                  -> a-commonForallCase (v,t) forallCon bodySubst (bndrs,cxt,body)-          | v `elem` (tyVarBndrGetName <$> bndrs) = forallCon bndrs cxt body -- shadowed+commonForallCase vt@(v,t) (bndrs,cxt,body)++            -- If a variable with the same name as the one to be replaced is bound by the forall, +            -- the variable to be replaced is shadowed in the body, so we leave the whole thing alone (no recursion)+          | v `elem` (tyVarBndrGetName <$> bndrs) = mkForall bndrs cxt body +           | otherwise =                let                   -- prevent capture@@ -216,10 +261,11 @@                   freshes = evades vars t                   freshTyVarBndrs = zipWith tyVarBndrSetName freshes bndrs                   substs = zip vars (VarT <$> freshes)-                  doSubsts f x = foldr f x substs+                  doSubsts :: SubstTypeVariable b => b -> b+                  doSubsts x = foldr subst x substs                                               in-                forallCon +                mkForall                    freshTyVarBndrs-                  (fmap (substInPred (v,t) . doSubsts substInPred) cxt ) -                  (     (  bodySubst (v,t) . doSubsts bodySubst  ) body)+                  (fmap (subst vt . doSubsts) cxt ) +                  (     (subst vt . doSubsts) body)
testing/Main.hs view
@@ -1,11 +1,14 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE TypeFamilies #-}+-- {-# OPTIONS -ddump-splices #-} module Main where  import Language.Haskell.TH.ExpandSyns import Language.Haskell.TH import Language.Haskell.TH.Syntax+import Util      -- type A = forall a. B a; type B a = Maybe a; expand [t|B A|] @@ -14,15 +17,47 @@ type C f = f Integer  -test1 = $(-    do-      t1 <-       [t| forall a. Show a => a -> B []            -> (Int,C []) |]-      expected <- [t| forall a. Show a => a -> (forall x. [] x) -> (Int,[] Integer) |]-      report False ("expected: "++pprint expected)-      actual <- expandSyns t1-      report False ("actual: "++pprint actual)-      if (pprint expected==pprint actual) then [| putStrLn "Ok" |] else [| error "expected /= actual" |] +$(sequence [tySynD (mkName "E") [PlainTV (mkName "x")]  +                (forallT'' ["y"] (conT ''Either `appT` varT' "x" `appT` varT' "y" --> conT ''Int))+           ]) - ) -main = test1+data family DF1 a++data instance DF1 Int = DInt (A ())++type family TF1 a++type instance TF1 Int = A ()++class Class1 a where+    type AT1 a++instance Class1 Int where type AT1 Int = A ()++type Int' = Int++main = do+    putStrLn "Basic test..."+    $(mkTest  [t| forall a. Show a => a -> B []             -> (Int,C []) |] +              [t| forall a. Show a => a -> (forall x. [] x) -> (Int,[] Integer) |])++    putStrLn "Variable capture avoidance test..."+    $(let+        expectedExpansion =+         forallT'' ["y_0"] (conT ''Either `appT` varT' "y" `appT` varT' "y_0" --> conT ''Int)+         -- the naive (and wrong) result would be:+         --   forall y. (forall y. Either y y -> Int)+      in+        mkTest  (forallT'' ["y"] (conT' "E" `appT` varT' "y")) +                (forallT'' ["y"] expectedExpansion))++    putStrLn "Testing that it doesn't crash on type families (expanding them is not supported yet)"+    $(let+        t = [t| (DF1 Int, TF1 Int, AT1 Int) |]+      in+        mkTest t t)+             +    putStrLn "Testing that the args of type family applications are handled" +    $(mkTest [t| (DF1 Int', TF1 Int', AT1 Int') |]+             [t| (DF1 Int, TF1 Int, AT1 Int) |])
th-expand-syns.cabal view
@@ -1,5 +1,5 @@ name:                th-expand-syns-version:             0.1.1.0+version:             0.2.0.0 synopsis:            Expands type synonyms in Template Haskell ASTs description:         Expands type synonyms in Template Haskell ASTs category:            Template Haskell