packages feed

free-foil 0.0.2 → 0.0.3

raw patch · 12 files changed

+868/−2 lines, 12 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Control.Monad.Foil: [RenameLeftNameBinder] :: forall (n :: S) (l :: S) (r :: S). (NameBinder n l -> NameBinder n r) -> UnifyNameBinders n l r
+ Control.Monad.Foil: [RenameRightNameBinder] :: forall (n :: S) (r :: S) (l :: S). (NameBinder n r -> NameBinder n l) -> UnifyNameBinders n l r
+ Control.Monad.Foil: [SameNameBinders] :: forall (n :: S) (l :: S). UnifyNameBinders n l l
+ Control.Monad.Foil: assertExt :: forall (n :: S) (l :: S). NameBinder n l -> ExtEvidence n l
+ Control.Monad.Foil: composeNameBinderRenamings :: forall (n :: S) (i :: S) (i' :: S) (l :: S) (l' :: S). (NameBinder n i -> NameBinder n i') -> (NameBinder i' l -> NameBinder i' l') -> NameBinder n l -> NameBinder n l'
+ Control.Monad.Foil: data UnifyNameBinders (n :: S) (l :: S) (r :: S)
+ Control.Monad.Foil: extendNameBinderRenaming :: forall pattern (i :: S) (n :: S) (n' :: S) (l :: S) r. CoSinkable pattern => (NameBinder i n -> NameBinder i n') -> pattern n l -> (forall (l' :: S). () => (NameBinder n' l -> NameBinder n' l') -> pattern n' l' -> r) -> r
+ Control.Monad.Foil: fromNameBinderRenaming :: forall (n :: S) (l :: S) (l' :: S). (NameBinder n l -> NameBinder n l') -> Name l -> Name l'
+ Control.Monad.Foil: unifyNameBinders :: forall (i :: S) (l :: S) (r :: S). NameBinder i l -> NameBinder i r -> UnifyNameBinders i l r
+ Control.Monad.Foil.Internal: [RenameLeftNameBinder] :: forall (n :: S) (l :: S) (r :: S). (NameBinder n l -> NameBinder n r) -> UnifyNameBinders n l r
+ Control.Monad.Foil.Internal: [RenameRightNameBinder] :: forall (n :: S) (r :: S) (l :: S). (NameBinder n r -> NameBinder n l) -> UnifyNameBinders n l r
+ Control.Monad.Foil.Internal: [SameNameBinders] :: forall (n :: S) (l :: S). UnifyNameBinders n l l
+ Control.Monad.Foil.Internal: assertExt :: forall (n :: S) (l :: S). NameBinder n l -> ExtEvidence n l
+ Control.Monad.Foil.Internal: composeNameBinderRenamings :: forall (n :: S) (i :: S) (i' :: S) (l :: S) (l' :: S). (NameBinder n i -> NameBinder n i') -> (NameBinder i' l -> NameBinder i' l') -> NameBinder n l -> NameBinder n l'
+ Control.Monad.Foil.Internal: data UnifyNameBinders (n :: S) (l :: S) (r :: S)
+ Control.Monad.Foil.Internal: extendNameBinderRenaming :: forall pattern (i :: S) (n :: S) (n' :: S) (l :: S) r. CoSinkable pattern => (NameBinder i n -> NameBinder i n') -> pattern n l -> (forall (l' :: S). () => (NameBinder n' l -> NameBinder n' l') -> pattern n' l' -> r) -> r
+ Control.Monad.Foil.Internal: fromNameBinderRenaming :: forall (n :: S) (l :: S) (l' :: S). (NameBinder n l -> NameBinder n l') -> Name l -> Name l'
+ Control.Monad.Foil.Internal: unifyNameBinders :: forall (i :: S) (l :: S) (r :: S). NameBinder i l -> NameBinder i r -> UnifyNameBinders i l r
+ Control.Monad.Foil.Relative: liftRM :: forall f m (b :: S) (a :: S). (RelMonad f m, Distinct b) => Scope b -> (f a -> f b) -> m a -> m b
+ Control.Monad.Foil.TH.Util: removeName :: VarBangType -> BangType
+ Control.Monad.Free.Foil: alphaEquiv :: forall (sig :: Type -> Type -> Type) (n :: S). (Bifunctor sig, Bifoldable sig, ZipMatch sig, Distinct n) => Scope n -> AST sig n -> AST sig n -> Bool
+ Control.Monad.Free.Foil: alphaEquivRefreshed :: forall (sig :: Type -> Type -> Type) (n :: S). (Bifunctor sig, Bifoldable sig, ZipMatch sig, Distinct n) => Scope n -> AST sig n -> AST sig n -> Bool
+ Control.Monad.Free.Foil: alphaEquivScoped :: forall (sig :: Type -> Type -> Type) (n :: S). (Bifunctor sig, Bifoldable sig, ZipMatch sig, Distinct n) => Scope n -> ScopedAST sig n -> ScopedAST sig n -> Bool
+ Control.Monad.Free.Foil: class ZipMatch (sig :: Type -> Type -> Type)
+ Control.Monad.Free.Foil: convertFromAST :: forall sig rawPattern rawScopedTerm rawTerm rawIdent (n :: S). Bifunctor sig => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm) -> (rawIdent -> rawTerm) -> (rawIdent -> rawPattern) -> (rawTerm -> rawScopedTerm) -> (Int -> rawIdent) -> AST sig n -> rawTerm
+ Control.Monad.Free.Foil: convertFromScopedAST :: forall sig rawPattern rawScopedTerm rawTerm rawIdent (n :: S). Bifunctor sig => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm) -> (rawIdent -> rawTerm) -> (rawIdent -> rawPattern) -> (rawTerm -> rawScopedTerm) -> (Int -> rawIdent) -> ScopedAST sig n -> (rawPattern, rawScopedTerm)
+ Control.Monad.Free.Foil: convertToAST :: forall (n :: S) sig rawIdent rawTerm rawPattern rawScopedTerm. (Distinct n, Bifunctor sig, Ord rawIdent) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (rawPattern -> Maybe rawIdent) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> rawTerm -> AST sig n
+ Control.Monad.Free.Foil: convertToScopedAST :: forall (n :: S) sig rawIdent rawTerm rawPattern rawScopedTerm. (Distinct n, Bifunctor sig, Ord rawIdent) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (rawPattern -> Maybe rawIdent) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> (rawPattern, rawScopedTerm) -> ScopedAST sig n
+ Control.Monad.Free.Foil: instance (Control.Monad.Free.Foil.ZipMatch f, Control.Monad.Free.Foil.ZipMatch g) => Control.Monad.Free.Foil.ZipMatch (Data.Bifunctor.Sum.Sum f g)
+ Control.Monad.Free.Foil: refreshAST :: forall (sig :: Type -> Type -> Type) (n :: S). (Bifunctor sig, Distinct n) => Scope n -> AST sig n -> AST sig n
+ Control.Monad.Free.Foil: refreshScopedAST :: forall (sig :: Type -> Type -> Type) (n :: S). (Bifunctor sig, Distinct n) => Scope n -> ScopedAST sig n -> ScopedAST sig n
+ Control.Monad.Free.Foil: substituteRefreshed :: forall (sig :: Type -> Type -> Type) (o :: S) (i :: S). (Bifunctor sig, Distinct o) => Scope o -> Substitution (AST sig) i o -> AST sig i -> AST sig o
+ Control.Monad.Free.Foil: unsafeEqAST :: forall (sig :: Type -> Type -> Type) (n :: S) (l :: S). (Bifoldable sig, ZipMatch sig) => AST sig n -> AST sig l -> Bool
+ Control.Monad.Free.Foil: unsafeEqScopedAST :: forall (sig :: Type -> Type -> Type) (n :: S) (l :: S). (Bifoldable sig, ZipMatch sig) => ScopedAST sig n -> ScopedAST sig l -> Bool
+ Control.Monad.Free.Foil: zipMatch :: ZipMatch sig => sig scope term -> sig scope' term' -> Maybe (sig (scope, scope') (term, term'))
+ Control.Monad.Free.Foil.TH.Convert: mkConvertFromFreeFoil :: Name -> Name -> Name -> Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.Convert: mkConvertFromSig :: Name -> Name -> Name -> Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.Convert: mkConvertToFreeFoil :: Name -> Name -> Name -> Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.Convert: mkConvertToSig :: Name -> Name -> Name -> Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.Convert: mkGetPatternBinder :: Name -> Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.Convert: mkGetScopedTerm :: Name -> Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.PatternSynonyms: mkPatternSynonym :: Type -> Name -> Name -> Con -> Q [Dec]
+ Control.Monad.Free.Foil.TH.PatternSynonyms: mkPatternSynonyms :: Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.Signature: mkSignature :: Name -> Name -> Name -> Name -> Q [Dec]
+ Control.Monad.Free.Foil.TH.ZipMatch: deriveZipMatch :: Name -> Q [Dec]

Files

+ ChangeLog.md view
@@ -0,0 +1,31 @@+# CHANGELOG for `free-foil`++# 0.0.3 — 2024-06-20++- Add α-equivalence checks and α-normalization (see [#12](https://github.com/fizruk/free-foil/pull/12)):++  - `Control.Monad.Foil` now offers more helpers to work with binder renaming and binder unification.+    These helpers can be used to implement (efficient) α-equivalence.+  - In `Control.Monad.Free.Foil` general implementation of α-equivalence and α-normalization is provided.++- Add general conversion functions for free foil (see [#14](https://github.com/fizruk/free-foil/pull/14)):++  - `Control.Monad.Free.Foil` now offers `convertToAST` and `convertFromAST` functions+    enabling easier implementation of conversions raw and scope-safe representations.++- Add Template Haskell functions for free foil (see [#14](https://github.com/fizruk/free-foil/pull/14)):++  - `Control.Monad.Free.Foil.TH` contains many useful functions to generate free foil from+    a raw representation (e.g. generated via BNFC), including generation of the signature,+    convenient pattern synonyms, `ZipMatch` instance, and conversion helpers.++# 0.0.2 — 2024-06-18++- Improve TH to support parametrized data types (see [#11](https://github.com/fizruk/free-foil/pull/11))+- Split `lambda-pi` into its own package (see [#10](https://github.com/fizruk/free-foil/pull/10))+- Switch to `template-haskell >= 2.21.0.0` (to support latest Stackage Nightly)+- Fix doctests (see [#9](https://github.com/fizruk/free-foil/pull/9))++# 0.0.1 — 2024-06-08++First release, corresponding to the ICCQ 2024 paper.
free-foil.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           free-foil-version:        0.0.2+version:        0.0.3 synopsis:       Efficient Type-Safe Capture-Avoiding Substitution for Free (Scoped Monads) description:    Please see the README on GitHub at <https://github.com/fizruk/free-foil#readme> category:       Parsing@@ -19,6 +19,7 @@ build-type:     Simple extra-source-files:     README.md+    ChangeLog.md     LICENSE  source-repository head@@ -39,6 +40,11 @@       Control.Monad.Foil.TH.Util       Control.Monad.Free.Foil       Control.Monad.Free.Foil.Example+      Control.Monad.Free.Foil.TH+      Control.Monad.Free.Foil.TH.Convert+      Control.Monad.Free.Foil.TH.PatternSynonyms+      Control.Monad.Free.Foil.TH.Signature+      Control.Monad.Free.Foil.TH.ZipMatch   other-modules:       Paths_free_foil   hs-source-dirs:
src/Control/Monad/Foil.hs view
@@ -28,11 +28,14 @@   withFresh,   withRefreshed,   unsinkName,-  -- * Safe (co)sinking+  -- * Safe (co)sinking and renaming   Sinkable(..),   CoSinkable(..),   sink,   extendRenaming,+  extendNameBinderRenaming,+  composeNameBinderRenamings,+  fromNameBinderRenaming,   extendRenamingNameBinder,   -- * Safe substitutions   Substitution,@@ -40,6 +43,9 @@   identitySubst,   addSubst,   addRename,+  -- * Unification of binders+  UnifyNameBinders(..),+  unifyNameBinders,   -- * Name maps   NameMap,   emptyNameMap,@@ -51,6 +57,7 @@   Distinct,   DistinctEvidence(..),   assertDistinct,+  assertExt,   DExt,   InjectName(..), ) where
src/Control/Monad/Foil/Internal.hs view
@@ -40,6 +40,7 @@ import qualified Data.IntSet     as IntSet import           Data.Kind       (Type) import           Unsafe.Coerce+import Data.Coerce (coerce)  -- * Safe types and operations @@ -134,6 +135,10 @@ assertDistinct :: Distinct n => NameBinder n l -> DistinctEvidence l assertDistinct _ = unsafeDistinct +-- | A distinct scope extended with a 'NameBinder' is also distinct.+assertExt :: NameBinder n l -> ExtEvidence n l+assertExt _ = unsafeExt+ -- | Safely rename (if necessary) a given name to extend a given scope. -- This is similar to 'withFresh', except if the name does not clash with -- the scope, it can be used immediately, without renaming.@@ -155,6 +160,37 @@   | nameOf binder == name = Nothing   | otherwise = Just (UnsafeName raw) +-- * Unification of binders++-- | Unification result for two binders,+-- extending some common scope to scopes @l@ and @r@ respectively.+--+-- Due to the implementation of the foil,+data UnifyNameBinders n l r where+  -- | Binders are the same, proving that type parameters @l@ and @r@+  -- are in fact equivalent.+  SameNameBinders :: UnifyNameBinders n l l+  -- | It is possible to safely rename the left binder+  -- to match the right one.+  RenameLeftNameBinder :: (NameBinder n l -> NameBinder n r) -> UnifyNameBinders n l r+  -- | It is possible to safely rename the right binder+  -- to match the left one.+  RenameRightNameBinder :: (NameBinder n r -> NameBinder n l) -> UnifyNameBinders n l r++-- | Unify binders either by asserting that they are the same,+-- or by providing a /safe/ renaming function to convert one binder to another.+unifyNameBinders+  :: forall i l r.+     NameBinder i l+  -> NameBinder i r+  -> UnifyNameBinders i l r+unifyNameBinders (UnsafeNameBinder (UnsafeName i1)) (UnsafeNameBinder (UnsafeName i2))+  | i1 == i2  = unsafeCoerce (SameNameBinders @l)  -- equal names extend scopes equally+  | i1 < i2   = RenameRightNameBinder $ \(UnsafeNameBinder (UnsafeName i'')) ->+      if i'' == i2 then UnsafeNameBinder (UnsafeName i1) else UnsafeNameBinder (UnsafeName i'')+  | otherwise = RenameLeftNameBinder $ \(UnsafeNameBinder (UnsafeName i')) ->+      if i'  == i1 then UnsafeNameBinder (UnsafeName i2) else UnsafeNameBinder (UnsafeName i')+ -- * Safe sinking  -- | Sinking an expression from scope @n@ into a (usualy extended) scope @l@,@@ -195,6 +231,31 @@   -> r extendRenaming _ pattern cont =   cont unsafeCoerce (unsafeCoerce pattern)++-- | Extend renaming of binders when going under a 'CoSinkable' pattern (generalized binder).+-- Note that the scope under pattern is independent of the codomain of the renaming.+extendNameBinderRenaming+  :: CoSinkable pattern+  => (NameBinder i n -> NameBinder i n')  -- ^ Map names from scope @n@ to a (possibly larger) scope @n'@.+  -> pattern n l          -- ^ A pattern that extends scope @n@ to another scope @l@.+  -> (forall l'. (NameBinder n' l -> NameBinder n' l') -> pattern n' l' -> r )+  -- ^ A continuation, accepting an extended renaming from @l@ to @l'@ (which itself extends @n'@)+  -- and a (possibly refreshed) pattern that extends @n'@ to @l'@.+  -> r+extendNameBinderRenaming _ pattern cont =+  cont unsafeCoerce (unsafeCoerce pattern)++-- | Safely compose renamings of name binders.+-- The underlying implementation is+composeNameBinderRenamings+  :: (NameBinder n i -> NameBinder n i')    -- ^ Rename binders extending scope @n@ from @i@ to @i'@.+  -> (NameBinder i' l -> NameBinder i' l')  -- ^ Rename binders extending scope @i'@ from @l@ to @l'@.+  -> (NameBinder n l -> NameBinder n l')+composeNameBinderRenamings = unsafeCoerce (flip (.))++-- | Convert renaming of name binders into renaming of names in the inner scopes.+fromNameBinderRenaming :: (NameBinder n l -> NameBinder n l') -> Name l -> Name l'+fromNameBinderRenaming = coerce  -- | Extend renaming when going under a 'NameBinder'. -- Note that the scope under binder is independent of the codomain of the renaming.
src/Control/Monad/Foil/Relative.hs view
@@ -21,3 +21,7 @@   -- Also, we could probably treat types in 'S' as singletons and extract distinct scopes that way,   -- preserving the more general type signature for 'rbind'.   rbind :: Distinct b => Scope b -> m a -> (f a -> m b) -> m b++-- | Relative version of 'liftM' (an 'fmap' restricted to 'Monad').+liftRM :: (RelMonad f m, Distinct b) => Scope b -> (f a -> f b) -> m a -> m b+liftRM scope f m = rbind scope m (rreturn . f)
src/Control/Monad/Foil/TH/Util.hs view
@@ -24,3 +24,6 @@ tvarName = \case   PlainTV name _ -> name   KindedTV name _ _ -> name++removeName :: VarBangType -> BangType+removeName (_name, bang_, type_) = (bang_, type_)
src/Control/Monad/Free/Foil.hs view
@@ -8,6 +8,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds             #-} {-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE ScopedTypeVariables   #-} {-# LANGUAGE StandaloneDeriving    #-} {-# LANGUAGE UndecidableInstances  #-} -- | This module defines a variation of@@ -20,7 +21,13 @@ import           Control.DeepSeq import qualified Control.Monad.Foil.Internal as Foil import qualified Control.Monad.Foil.Relative as Foil+import           Data.Bifoldable import           Data.Bifunctor+import           Data.Bifunctor.Sum+import           Data.Coerce                 (coerce)+import           Data.Map                    (Map)+import qualified Data.Map                    as Map+import           Data.Monoid                 (All (..)) import           GHC.Generics                (Generic)  -- | Scoped term under a (single) name binder.@@ -53,6 +60,8 @@ instance Foil.InjectName (AST sig) where   injectName = Var +-- * Substitution+ -- | Substitution for free (scoped monads). substitute   :: (Bifunctor sig, Foil.Distinct o)@@ -71,6 +80,31 @@             body' = substitute scope' subst' body         in ScopedAST binder' body' +-- | Substitution for free (scoped monads).+--+-- This is a version of 'substitute' that forces refreshing of all name binders,+-- resulting in a term with normalized binders:+--+-- > substituteRefreshed scope subst = refreshAST scope . subtitute scope subst+--+-- In general, 'substitute' is more efficient since it does not always refresh binders.+substituteRefreshed+  :: (Bifunctor sig, Foil.Distinct o)+  => Foil.Scope o+  -> Foil.Substitution (AST sig) i o+  -> AST sig i+  -> AST sig o+substituteRefreshed scope subst = \case+  Var name -> Foil.lookupSubst subst name+  Node node -> Node (bimap f (substituteRefreshed scope subst) node)+  where+    f (ScopedAST binder body) =+      Foil.withFresh scope $ \binder' ->+        let subst' = Foil.addRename (Foil.sink subst) binder (Foil.nameOf binder')+            scope' = Foil.extendScope binder' scope+            body' = substituteRefreshed scope' subst' body+        in ScopedAST binder' body'+ -- | @'AST' sig@ is a monad relative to 'Foil.Name'. instance Bifunctor sig => Foil.RelMonad Foil.Name (AST sig) where   rreturn = Var@@ -87,3 +121,200 @@                           Nothing -> Foil.rreturn (Foil.nameOf binder')                           Just n  -> Foil.sink (subst n)            in ScopedAST binder' (Foil.rbind scope' body subst')++-- * \(\alpha\)-equivalence++-- | Refresh (force) all binders in a term, minimizing the used indices.+refreshAST+  :: (Bifunctor sig, Foil.Distinct n)+  => Foil.Scope n+  -> AST sig n+  -> AST sig n+refreshAST scope = \case+  t@Var{} -> t+  Node t -> Node (bimap (refreshScopedAST scope) (refreshAST scope) t)++-- | Similar to `refreshAST`, but for scoped terms.+refreshScopedAST :: (Bifunctor sig, Foil.Distinct n)+  => Foil.Scope n+  -> ScopedAST sig n+  -> ScopedAST sig n+refreshScopedAST scope (ScopedAST binder body) =+  Foil.withFresh scope $ \binder' ->+    let scope' = Foil.extendScope binder' scope+        subst = Foil.addRename (Foil.sink Foil.identitySubst) binder (Foil.nameOf binder')+    in ScopedAST binder' (substituteRefreshed scope' subst body)++-- | \(\alpha\)-equivalence check for two terms in one scope+-- via normalization of bound identifiers (via 'refreshAST').+--+-- Compared to 'alphaEquiv', this function may perform some unnecessary+-- changes of bound variables when the binders are the same on both sides.+alphaEquivRefreshed+  :: (Bifunctor sig, Bifoldable sig, ZipMatch sig, Foil.Distinct n)+  => Foil.Scope n+  -> AST sig n+  -> AST sig n+  -> Bool+alphaEquivRefreshed scope t1 t2 = refreshAST scope t1 `unsafeEqAST` refreshAST scope t2++-- | \(\alpha\)-equivalence check for two terms in one scope+-- via unification of bound variables (via 'unifyNameBinders').+--+-- Compared to 'alphaEquivRefreshed', this function might skip unnecessary+-- changes of bound variables when both binders in two matching scoped terms coincide.+alphaEquiv+  :: (Bifunctor sig, Bifoldable sig, ZipMatch sig, Foil.Distinct n)+  => Foil.Scope n+  -> AST sig n+  -> AST sig n+  -> Bool+alphaEquiv _scope (Var x) (Var y) = x == coerce y+alphaEquiv scope (Node l) (Node r) =+  case zipMatch l r of+    Nothing -> False+    Just tt -> getAll (bifoldMap (All . uncurry (alphaEquivScoped scope)) (All . uncurry (alphaEquiv scope)) tt)+alphaEquiv _ _ _ = False++-- | Same as 'alphaEquiv' but for scoped terms.+alphaEquivScoped+  :: (Bifunctor sig, Bifoldable sig, ZipMatch sig, Foil.Distinct n)+  => Foil.Scope n+  -> ScopedAST sig n+  -> ScopedAST sig n+  -> Bool+alphaEquivScoped scope+  (ScopedAST binder1 body1)+  (ScopedAST binder2 body2) =+    case Foil.unifyNameBinders binder1 binder2 of+      -- if binders are the same, then we can safely compare bodies+      Foil.SameNameBinders ->  -- after seeing this we know that body scopes are the same+        case Foil.assertDistinct binder1 of+          Foil.Distinct ->+            let scope1 = Foil.extendScope binder1 scope+            in alphaEquiv scope1 body1 body2+      -- if we can safely rename first binder into second+      Foil.RenameLeftNameBinder rename1to2 ->+        case Foil.assertDistinct binder2 of+          Foil.Distinct ->+            let scope2 = Foil.extendScope binder2 scope+            in alphaEquiv scope2 (Foil.liftRM scope2 (Foil.fromNameBinderRenaming rename1to2) body1) body2+      -- if we can safely rename second binder into first+      Foil.RenameRightNameBinder rename2to1 ->+        case Foil.assertDistinct binder1 of+          Foil.Distinct ->+            let scope1 = Foil.extendScope binder1 scope+            in alphaEquiv scope1 body1 (Foil.liftRM scope1 (Foil.fromNameBinderRenaming rename2to1) body2)++-- ** Unsafe equality checks++-- | /Unsafe/ equality check for two terms.+-- This check ignores the possibility that two terms might have different+-- scope extensions under binders (which might happen due to substitution+-- under a binder in absence of name conflicts).+unsafeEqAST+  :: (Bifoldable sig, ZipMatch sig)+  => AST sig n+  -> AST sig l+  -> Bool+unsafeEqAST (Var x) (Var y) = x == coerce y+unsafeEqAST (Node t1) (Node t2) =+  case zipMatch t1 t2 of+    Nothing -> False+    Just tt -> getAll (bifoldMap (All . uncurry unsafeEqScopedAST) (All . uncurry unsafeEqAST) tt)+unsafeEqAST _ _ = False++-- | A version of 'unsafeEqAST' for scoped terms.+unsafeEqScopedAST+  :: (Bifoldable sig, ZipMatch sig)+  => ScopedAST sig n+  -> ScopedAST sig l+  -> Bool+unsafeEqScopedAST (ScopedAST binder1 body1) (ScopedAST binder2 body2) = and+  [ binder1 == coerce binder2+  , body1 `unsafeEqAST` body2+  ]++-- ** Syntactic matching (unification)++-- | Perform one-level matching for the two (non-variable) terms.+class ZipMatch sig where+  zipMatch+    :: sig scope term     -- ^ Left non-variable term.+    -> sig scope' term'   -- ^ Right non-variable term.+    -> Maybe (sig (scope, scope') (term, term'))++instance (ZipMatch f, ZipMatch g) => ZipMatch (Sum f g) where+  zipMatch (L2 f) (L2 f') = L2 <$> zipMatch f f'+  zipMatch (R2 g) (R2 g') = R2 <$> zipMatch g g'+  zipMatch _ _            = Nothing++-- * Converting to and from free foil++-- ** Convert to free foil++convertToAST+  :: (Foil.Distinct n, Bifunctor sig, Ord rawIdent)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -> (rawPattern -> Maybe rawIdent)+  -> (rawScopedTerm -> rawTerm)+  -> Foil.Scope n+  -> Map rawIdent (Foil.Name n) -> rawTerm -> AST sig n+convertToAST toSig getPatternBinder getScopedTerm scope names t =+  case toSig t of+    Left x ->+      case Map.lookup x names of+        Nothing   -> error "undefined variable"+        Just name -> Var name+    Right node -> Node $+      bimap+        (convertToScopedAST toSig getPatternBinder getScopedTerm scope names)+        (convertToAST toSig getPatternBinder getScopedTerm scope names)+        node++convertToScopedAST+  :: (Foil.Distinct n, Bifunctor sig, Ord rawIdent)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -> (rawPattern -> Maybe rawIdent)+  -> (rawScopedTerm -> rawTerm)+  -> Foil.Scope n+  -> Map rawIdent (Foil.Name n)+  -> (rawPattern, rawScopedTerm)+  -> ScopedAST sig n+convertToScopedAST toSig getPatternBinder getScopedTerm scope names (pat, scopedTerm) =+  Foil.withFresh scope $ \binder ->+    let scope' = Foil.extendScope binder scope+        names' =+          case getPatternBinder pat of+            Nothing -> Foil.sink <$> names+            Just x  -> Map.insert x (Foil.nameOf binder) (Foil.sink <$> names)+     in ScopedAST binder (convertToAST toSig getPatternBinder getScopedTerm scope' names' (getScopedTerm scopedTerm))++-- ** Convert from free foil++convertFromAST+  :: Bifunctor sig+  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)+  -> (rawIdent -> rawTerm)+  -> (rawIdent -> rawPattern)+  -> (rawTerm -> rawScopedTerm)+  -> (Int -> rawIdent) -> AST sig n -> rawTerm+convertFromAST fromSig fromVar makePattern makeScoped f = \case+  Var x -> fromVar (f (Foil.nameId x))+  Node node -> fromSig $+    bimap+      (convertFromScopedAST fromSig fromVar makePattern makeScoped f)+      (convertFromAST fromSig fromVar makePattern makeScoped f)+      node++convertFromScopedAST+  :: Bifunctor sig+  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)+  -> (rawIdent -> rawTerm)+  -> (rawIdent -> rawPattern)+  -> (rawTerm -> rawScopedTerm)+  -> (Int -> rawIdent) -> ScopedAST sig n -> (rawPattern, rawScopedTerm)+convertFromScopedAST fromSig fromVar makePattern makeScoped f = \case+  ScopedAST binder body ->+    ( makePattern (f (Foil.nameId (Foil.nameOf binder)))+    , makeScoped (convertFromAST fromSig fromVar makePattern makeScoped f body))
+ src/Control/Monad/Free/Foil/TH.hs view
@@ -0,0 +1,11 @@+module Control.Monad.Free.Foil.TH (+  module Control.Monad.Free.Foil.TH.Signature,+  module Control.Monad.Free.Foil.TH.PatternSynonyms,+  module Control.Monad.Free.Foil.TH.Convert,+  module Control.Monad.Free.Foil.TH.ZipMatch,+) where++import Control.Monad.Free.Foil.TH.Signature+import Control.Monad.Free.Foil.TH.PatternSynonyms+import Control.Monad.Free.Foil.TH.Convert+import Control.Monad.Free.Foil.TH.ZipMatch
+ src/Control/Monad/Free/Foil/TH/Convert.hs view
@@ -0,0 +1,272 @@+{-# LANGUAGE LambdaCase      #-}+{-# OPTIONS_GHC -fno-warn-type-defaults      #-}+{-# LANGUAGE TemplateHaskell #-}+module Control.Monad.Free.Foil.TH.Convert where++import           Language.Haskell.TH+import           Language.Haskell.TH.Syntax++import           Control.Monad.Foil.TH.Util++-- * Bulk generators++-- | Generate helpers for conversion to scope-safe representation.+-- Includes 'mkConvertToSig', 'mkGetPatternBinder', and 'mkGetScopedTerm'.+mkConvertToFreeFoil+  :: Name -- ^ Type name for raw terms.+  -> Name -- ^ Type name for raw variable identifiers.+  -> Name -- ^ Type name for raw scoped terms.+  -> Name -- ^ Type name for raw patterns.+  -> Q [Dec]+mkConvertToFreeFoil termT nameT scopeT patternT = concat <$> sequence+  [ mkConvertToSig termT nameT scopeT patternT+  , mkGetPatternBinder nameT patternT+  , mkGetScopedTerm termT scopeT+  ]++-- | Generate helpers for conversion from scope-safe representation.+-- Includes 'mkConvertFromSig'.+mkConvertFromFreeFoil+  :: Name -- ^ Type name for raw terms.+  -> Name -- ^ Type name for raw variable identifiers.+  -> Name -- ^ Type name for raw scoped terms.+  -> Name -- ^ Type name for raw patterns.+  -> Q [Dec]+mkConvertFromFreeFoil termT nameT scopeT patternT = concat <$> sequence+  [ mkConvertFromSig termT nameT scopeT patternT+  ]++-- * Individual generators++-- | Generate conversion helper that goes unpeels one node from a raw term.+mkConvertToSig+  :: Name -- ^ Type name for raw terms.+  -> Name -- ^ Type name for raw variable identifiers.+  -> Name -- ^ Type name for raw scoped terms.+  -> Name -- ^ Type name for raw patterns.+  -> Q [Dec]+mkConvertToSig termT nameT scopeT patternT = do+  TyConI (DataD _ctx _name termTVars _kind termCons _deriv) <- reify termT+  TyConI (NewtypeD _ctx _name nameTVars _kind _nameCons _deriv) <- reify nameT++  convertTermToSigClauses <- concat <$> mapM toClause termCons++  let params = map (VarT . tvarName) termTVars+      termType = return $ PeelConT termT params+      nameType = return $ PeelConT nameT (map (VarT . tvarName) nameTVars)+      scopeType = return $ PeelConT scopeT params+      patternType = return $ PeelConT patternT params+      signatureType = return $ PeelConT signatureT params+  convertTermToSigClausesType <-+    [t| $termType -> Either $nameType ($signatureType ($patternType, $scopeType) $termType) |]++  addModFinalizer $ putDoc (DeclDoc convertTermToSigT)+    ("/Generated/ with '" ++ show 'mkConvertToFreeFoil ++ "'. Perform one step of converting '" ++ show termT ++ "', peeling off one node of type '" ++ show signatureT ++ "'.")+  return+    [ SigD convertTermToSigT convertTermToSigClausesType+    , FunD convertTermToSigT convertTermToSigClauses+    ]+  where+    signatureT = mkName (nameBase termT ++ "Sig")+    convertTermToSigT = mkName ("convertTo" ++ nameBase signatureT)++    toClause :: Con -> Q [Clause]+    toClause = \case+      NormalC conName types | any isVarP pats+        -> return [Clause [ConP conName [] pats] (NormalB (AppE (ConE 'Left) (VarE x))) []]+        where+          x = mkName "x"+          isVarP VarP{} = True+          isVarP _ = False+          pats =+            [ case type_ of+                PeelConT typeName _ | typeName == nameT -> VarP x+                _ -> WildP+            | (_bang, type_) <- types ]+      NormalC conName types -> mkClause conName conName' types+        where+          conName' = mkName (nameBase conName ++ "Sig")+      RecC conName types -> toClause (NormalC conName (map removeName types))+      InfixC l conName r -> mkClause conName conName' [l, r]+        where+          conName' = mkName (nameBase conName ++ "---")+      ForallC _ _ con -> toClause con+      GadtC conNames types _retType -> concat <$> mapM (\conName -> toClause (NormalC conName types)) conNames+      RecGadtC conNames types retType -> toClause (GadtC conNames (map removeName types) retType)++    mkClause conName conName' types = return+      [ Clause [ConP conName [] pats] (NormalB (AppE (ConE 'Right) (foldl AppE (ConE conName') (concat args)))) [] ]+      where+        p = mkName "p"+        (pats, args) = unzip+          [ case type_ of+              PeelConT typeName _+                | typeName == patternT -> (VarP p, [])+                | typeName == scopeT -> (VarP x, [TupE [Just (VarE p), Just (VarE x)]])+              _ -> (VarP x, [VarE x])+          | (i, (_bang, type_)) <- zip [0..] types+          , let x = mkName ("x" ++ show i)+          ]++-- | Generate conversion helper that peels back one node to a raw term.+mkConvertFromSig+  :: Name -- ^ Type name for raw terms.+  -> Name -- ^ Type name for raw variable identifiers.+  -> Name -- ^ Type name for raw scoped terms.+  -> Name -- ^ Type name for raw patterns.+  -> Q [Dec]+mkConvertFromSig termT nameT scopeT patternT = do+  TyConI (DataD _ctx _name termTVars _kind termCons _deriv) <- reify termT+  TyConI (NewtypeD _ctx _name _nameTVars _kind _nameCons _deriv) <- reify nameT++  convertTermFromSigClauses <- concat <$> mapM toClause termCons++  let params = map (VarT . tvarName) termTVars+      termType = return $ PeelConT termT params+      scopeType = return $ PeelConT scopeT params+      patternType = return $ PeelConT patternT params+      signatureType = return $ PeelConT signatureT params+  convertTermFromSigClausesType <-+    [t| $signatureType ($patternType, $scopeType) $termType -> $termType |]++  addModFinalizer $ putDoc (DeclDoc convertTermFromSigT)+    ("/Generated/ with '" ++ show 'mkConvertToFreeFoil ++ "'. Perform one step of converting '" ++ show termT ++ "', peeling off one node of type '" ++ show signatureT ++ "'.")+  return+    [ SigD convertTermFromSigT convertTermFromSigClausesType+    , FunD convertTermFromSigT convertTermFromSigClauses+    ]+  where+    signatureT = mkName (nameBase termT ++ "Sig")+    convertTermFromSigT = mkName ("convertFrom" ++ nameBase signatureT)++    toClause :: Con -> Q [Clause]+    toClause = \case+      NormalC _conName types | or [ typeName == nameT | (_bang, PeelConT typeName _typeParams) <- types ]+        -> pure []+      NormalC conName types -> mkClause conName conName' types+        where+          conName' = mkName (nameBase conName ++ "Sig")+      RecC conName types -> toClause (NormalC conName (map removeName types))+      InfixC l conName r -> mkClause conName conName' [l, r]+        where+          conName' = mkName (nameBase conName ++ "---")+      ForallC _ _ con -> toClause con+      GadtC conNames types _retType -> concat <$> mapM (\conName -> toClause (NormalC conName types)) conNames+      RecGadtC conNames types retType -> toClause (GadtC conNames (map removeName types) retType)++    mkClause conName conName' types = return+      [ Clause [ConP conName' [] (concat pats)] (NormalB (foldl AppE (ConE conName) args)) [] ]+      where+        p = mkName "p"+        (args, pats) = unzip+          [ case type_ of+              PeelConT typeName _+                | typeName == patternT -> (VarE p, [])+                | typeName == scopeT -> (VarE x, [TupP [VarP p, VarP x]])+              _ -> (VarE x, [VarP x])+          | (i, (_bang, type_)) <- zip [0..] types+          , let x = mkName ("x" ++ show i)+          ]++-- | Generate a helper that extracts at most one binder from a pattern.+mkGetPatternBinder+  :: Name -- ^ Type name for raw variable identifiers.+  -> Name -- ^ Type name for raw patterns.+  -> Q [Dec]+mkGetPatternBinder nameT patternT = do+  TyConI (NewtypeD _ctx _name nameTVars _kind _nameCons _deriv) <- reify nameT+  TyConI (DataD _ctx _name patternTVars _kind patternCons _deriv) <- reify patternT++  getPatternBinderClauses <- concat <$> mapM toClause patternCons++  let nameType = return $ PeelConT nameT (map (VarT . tvarName) nameTVars)+      patternType = return $ PeelConT patternT (map (VarT . tvarName) patternTVars)+  getPatternBinderClausesType <-+    [t| $patternType -> Maybe $nameType |]++  addModFinalizer $ putDoc (DeclDoc getPatternBinderT)+    ("/Generated/ with '" ++ show 'mkGetPatternBinder ++ "'. Extract at most one binder from a pattern or __crash__.")+  return+    [ SigD getPatternBinderT getPatternBinderClausesType+    , FunD getPatternBinderT getPatternBinderClauses+    ]+  where+    getPatternBinderT = mkName ("get" ++ nameBase patternT ++ "Binder")++    toClause :: Con -> Q [Clause]+    toClause = \case+      NormalC conName types -> mkClause conName types+      RecC conName types -> toClause (NormalC conName (map removeName types))+      InfixC l conName r -> toClause (NormalC conName [l, r])+      ForallC _ _ con -> toClause con+      GadtC conNames types _retType -> concat <$> mapM (\conName -> mkClause conName types) conNames+      RecGadtC conNames types retType -> toClause (GadtC conNames (map removeName types) retType)++    mkClause :: Name -> [BangType] -> Q [Clause]+    mkClause conName types = do+      body <- case concat vars of+        [] -> [e| Nothing |]+        [Just y] -> [e| Just $y |]+        _ -> [e| error "complex patterns are not supported" |]+      return [ Clause [ConP conName [] pats] (NormalB body) [] ]+      where+        x = mkName "x"+        (pats, vars) = unzip+          [ case type_ of+              PeelConT typeName _+                | typeName == nameT -> (VarP x, [Just (return (VarE x))])+                | typeName == patternT -> (WildP, [Nothing])+              _ -> (WildP, [])+          | (_bang, type_) <- types+          ]++-- | Generate a helper that extracts a term from a scoped term.+mkGetScopedTerm+  :: Name -- ^ Type name for raw terms.+  -> Name -- ^ Type name for raw scoped terms.+  -> Q [Dec]+mkGetScopedTerm termT scopeT = do+  TyConI (DataD _ctx _name termTVars _kind _termCons _deriv) <- reify termT+  TyConI (DataD _ctx _name _scopeTVars _kind scopeCons _deriv) <- reify scopeT++  getScopedTermClauses <- concat <$> mapM toClause scopeCons++  let params = map (VarT . tvarName) termTVars+      termType = return $ PeelConT termT params+      scopeType = return $ PeelConT scopeT params+  getScopedTermClausesType <-+    [t| $scopeType -> $termType |]++  addModFinalizer $ putDoc (DeclDoc getScopedTermT)+    ("/Generated/ with '" ++ show 'mkGetScopedTerm ++ "'. Extract scoped term or __crash__.")+  return+    [ SigD getScopedTermT getScopedTermClausesType+    , FunD getScopedTermT getScopedTermClauses+    ]+  where+    getScopedTermT = mkName ("get" ++ nameBase termT ++ "From" ++ nameBase scopeT)++    toClause :: Con -> Q [Clause]+    toClause = \case+      NormalC conName types -> mkClause conName types+      RecC conName types -> toClause (NormalC conName (map removeName types))+      InfixC l conName r -> toClause (NormalC conName [l, r])+      ForallC _ _ con -> toClause con+      GadtC conNames types _retType -> concat <$> mapM (\conName -> mkClause conName types) conNames+      RecGadtC conNames types retType -> toClause (GadtC conNames (map removeName types) retType)++    mkClause :: Name -> [BangType] -> Q [Clause]+    mkClause conName types = do+      body <- case concat vars of+        [y] -> [e| $y |]+        _ -> [e| error "complex patterns are not supported" |]+      return [ Clause [ConP conName [] pats] (NormalB body) [] ]+      where+        x = mkName "x"+        (pats, vars) = unzip+          [ case type_ of+              PeelConT typeName _+                | typeName == termT -> (VarP x, [return (VarE x)])+              _ -> (WildP, [])+          | (_bang, type_) <- types+          ]
+ src/Control/Monad/Free/Foil/TH/PatternSynonyms.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE LambdaCase      #-}+{-# OPTIONS_GHC -fno-warn-type-defaults      #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ViewPatterns    #-}+module Control.Monad.Free.Foil.TH.PatternSynonyms where++import Control.Monad (forM_)+import qualified Control.Monad.Foil         as Foil+import           Control.Monad.Foil.TH.Util+import           Control.Monad.Free.Foil+import           Language.Haskell.TH+import           Language.Haskell.TH.Syntax++-- | Generate helpful pattern synonyms given a signature bifunctor.+mkPatternSynonyms+  :: Name -- ^ Type name for the signature bifunctor.+  -> Q [Dec]+mkPatternSynonyms signatureT = do+  TyConI (DataD _ctx _name signatureTVars _kind signatureCons _deriv) <- reify signatureT++  case reverse signatureTVars of+    (tvarName -> term) : (tvarName -> scope) : (reverse -> params) -> do+      concat <$> mapM (mkPatternSynonym (PeelConT signatureT (map (VarT . tvarName) params)) scope term) signatureCons+    _ -> fail "cannot generate pattern synonyms"++mkPatternSynonym :: Type -> Name -> Name -> Con -> Q [Dec]+mkPatternSynonym signatureType scope term = \case+  NormalC conName types -> mkPatternSynonym signatureType scope term+    (GadtC [conName] types (AppT (AppT signatureType (VarT scope)) (VarT term)))++  RecC conName types -> mkPatternSynonym signatureType scope term (NormalC conName (map removeName types))++  InfixC l conName r -> mkPatternSynonym signatureType scope term (NormalC conName [l, r])++  ForallC params ctx con -> do+    [ PatSynSigD patName patType, patD ] <- mkPatternSynonym signatureType scope term con+    return+      [ PatSynSigD patName (ForallT params ctx patType)+      , patD+      ]++  GadtC conNames types _retType -> do+    let argsWithTypes = zipWith toPatternArgType [0..] types+        argsWithTypes' = foldMap collapse argsWithTypes+        pats   = map toArg argsWithTypes+        args  = map fst argsWithTypes'+        types' = map snd argsWithTypes'+    forM_ conNames $ \conName ->+      addModFinalizer $ putDoc (DeclDoc (mkPatternName conName))+        ("/Generated/ with '" ++ show 'mkPatternSynonyms ++ "'. Pattern synonym for an '" ++ show ''AST ++ "' node of type '" ++ show conName ++ "'.")+    return $ concat+      [ [ PatSynSigD patternName (foldr (AppT . AppT ArrowT) termType types')+        , PatSynD  patternName (PrefixPatSyn args) ImplBidir (ConP 'Node [] [ConP conName [] pats])+        ]+      | conName <- conNames+      , let patternName = mkPatternName conName+      ]++  RecGadtC conNames types retType -> mkPatternSynonym signatureType scope term (GadtC conNames (map removeName types) retType)++  where+    n = mkName "n"+    termType = PeelConT ''AST [signatureType, VarT n]+    toArg = \case+      Left ((b, _), (x, _)) -> ConP 'ScopedAST [] [VarP b, VarP x]+      Right (x, _) -> VarP x++    toPatternArgType i (_bang, VarT typeName)+      | typeName == scope =+          Left+            ( (mkName ("b" ++ show i), PeelConT ''Foil.NameBinder [VarT n, VarT l])+            , (mkName ("x" ++ show i), PeelConT ''AST [signatureType, VarT l]))+      | typeName == term =+          Right (mkName ("x" ++ show i), PeelConT ''AST [signatureType, VarT n])+      where+        l = mkName ("l" ++ show i)+    toPatternArgType i (_bang, type_)+      = Right (mkName ("z" ++ show i), type_)++    mkPatternName conName = mkName (dropEnd (length "Sig") (nameBase conName))+    dropEnd k = reverse . drop k . reverse++    collapse = \case+      Left (x, y) -> [x, y]+      Right x -> [x]
+ src/Control/Monad/Free/Foil/TH/Signature.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE LambdaCase      #-}+{-# LANGUAGE TemplateHaskell #-}+module Control.Monad.Free.Foil.TH.Signature where++import           Language.Haskell.TH+import           Language.Haskell.TH.Syntax++import           Control.Monad              (forM_)+import           Control.Monad.Foil.TH.Util+import           Data.Maybe                 (catMaybes)++-- | Generate a signature for the free foil (or free scoped monads)+-- based on a naïve recursive abstract syntax representation,+-- with clearly separated types for terms, variable identifiers, scoped terms,+-- and patterns (binders).+mkSignature+  :: Name -- ^ Type name for raw terms.+  -> Name -- ^ Type name for raw variable identifiers.+  -> Name -- ^ Type name for raw scoped terms.+  -> Name -- ^ Type name for raw patterns.+  -> Q [Dec]+mkSignature termT nameT scopeT patternT = do+  scope <- newName "scope"+  term <- newName "term"+  TyConI (DataD _ctx _name termTVars _kind termCons _deriv) <- reify termT++  signatureCons <- catMaybes <$> mapM (toSignatureCons scope term) termCons++  addModFinalizer $ putDoc (DeclDoc signatureT)+    ("/Generated/ with '" ++ show 'mkSignature ++ "'. A signature bifunctor, specifying the nodes of a syntax tree corresponding to '" ++ show termT ++ "'.")+  return+    [ DataD [] signatureT (termTVars ++ [PlainTV scope BndrReq, PlainTV term BndrReq]) Nothing signatureCons+      [DerivClause Nothing [ConT ''Functor, ConT ''Foldable, ConT ''Traversable]]+    ]+  where+    signatureT = mkName (nameBase termT ++ "Sig")++    toSignatureCons :: Name -> Name -> Con -> Q (Maybe Con)+    toSignatureCons scope term con' = case con' of+      -- treat constructors with a single variable field as variable constructor and ignore+      NormalC _conName types | or [ typeName == nameT | (_bang, PeelConT typeName _typeParams) <- types ]+        -> pure Nothing+      RecC _conName types | or [ typeName == nameT | (_name, _bang, PeelConT typeName _typeParams) <- types ]+        -> pure Nothing++      NormalC conName params -> do+        addModFinalizer $ putDoc (DeclDoc conName') ("Corresponds to '" ++ show conName ++ "'.")+        Just . NormalC conName' . catMaybes <$> mapM toSignatureParam params+        where+          conName' = mkName (nameBase conName ++ "Sig")+      RecC conName params -> do+        addModFinalizer $ putDoc (DeclDoc conName') ("Corresponds to '" ++ show conName ++ "'.")+        Just . RecC conName' . catMaybes <$> mapM toSignatureParam' params+        where+          conName' = mkName (nameBase conName ++ "Sig")+      InfixC l conName r -> do+        addModFinalizer $ putDoc (DeclDoc conName') ("Corresponds to '" ++ show conName ++ "'.")+        Just <$> (flip InfixC conName' <$> toInfixParam l <*> toInfixParam r)+        where+          conName' = mkName (nameBase conName ++ "---")+      ForallC params ctx con -> fmap (ForallC params ctx) <$> toSignatureCons scope term con+      GadtC conNames argTypes retType -> do+        let conNames' = map (\conName -> mkName (nameBase conName ++ "---")) conNames+        forM_ (zip conNames conNames') $ \(conName, conName') ->+          addModFinalizer $ putDoc (DeclDoc conName') ("Corresponds to '" ++ show conName ++ "'.")+        Just <$> (GadtC conNames' <$> (catMaybes <$> mapM toSignatureParam argTypes) <*> retType')+        where+          retType' = case retType of+            PeelConT typeName typeParams+              | typeName == termT -> return (PeelConT signatureT (typeParams ++ [VarT scope, VarT term]))+            _ -> fail "unexpected return type in a GADT constructor"+      RecGadtC conNames argTypes retType -> do+        let conNames' = map (\conName -> mkName (nameBase conName ++ "---")) conNames+        forM_ (zip conNames conNames') $ \(conName, conName') ->+          addModFinalizer $ putDoc (DeclDoc conName') ("Corresponds to '" ++ show conName ++ "'.")+        Just <$> (RecGadtC conNames' <$> (catMaybes <$> mapM toSignatureParam' argTypes) <*> retType')+        where+          retType' = case retType of+            PeelConT typeName typeParams+              | typeName == termT -> return (PeelConT signatureT (typeParams ++ [VarT scope, VarT term]))+            _ -> fail "unexpected return type in a GADT constructor"++      where+        toInfixParam (bang_, type_) = toSignatureParam (bang_, type_) >>= \case+          Nothing -> pure (bang_, VarT ''())+          Just bt -> pure bt++        toSignatureParam' (name, bang_, type_) = fmap k <$> toSignatureParam (bang_, type_)+          where+            k (x, y) = (name, x, y)++        toSignatureParam (bang_, PeelConT typeName _typeParams)+          | typeName == nameT = fail ("variable with other stuff in constructor: " ++ show con')+          | typeName == patternT = pure Nothing -- skip binders, they will be inserted automatically with each scoped term+          | typeName == scopeT = pure (Just (bang_, VarT scope))+          | typeName == termT = pure (Just (bang_, VarT term))+        toSignatureParam bt = pure (Just bt)  -- everything else remains as is
+ src/Control/Monad/Free/Foil/TH/ZipMatch.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE LambdaCase      #-}+{-# OPTIONS_GHC -fno-warn-type-defaults      #-}+{-# LANGUAGE ViewPatterns      #-}+{-# LANGUAGE TemplateHaskell #-}+module Control.Monad.Free.Foil.TH.ZipMatch where++import           Language.Haskell.TH++import           Control.Monad.Foil.TH.Util+import           Control.Monad.Free.Foil++-- | Generate 'ZipMatch' instance for a given bifunctor.+deriveZipMatch+  :: Name -- ^ Type name for the signature bifunctor.+  -> Q [Dec]+deriveZipMatch signatureT = do+  TyConI (DataD _ctx _name signatureTVars _kind signatureCons _deriv) <- reify signatureT++  case reverse signatureTVars of+    (tvarName -> term) : (tvarName -> scope) : (reverse -> params) -> do+      let signatureType = PeelConT signatureT (map (VarT . tvarName) params)+      clauses <- concat <$> mapM (toClause scope term) signatureCons+      let defaultClause = Clause [WildP, WildP] (NormalB (ConE 'Nothing)) []+      let instType = AppT (ConT ''ZipMatch) signatureType++      return+        [ InstanceD Nothing [] instType+          [ FunD 'zipMatch (clauses ++ [defaultClause]) ]+        ]+    _ -> fail "cannot generate pattern synonyms"++  where+    toClause :: Name -> Name -> Con -> Q [Clause]+    toClause scope term = go+      where+        go = \case+          NormalC conName types -> mkClause conName types+          RecC conName types -> go (NormalC conName (map removeName types))+          InfixC l conName r -> go (NormalC conName [l, r])+          ForallC _ _ con -> go con+          GadtC conNames types _retType -> concat <$> mapM (\conName -> mkClause conName types) conNames+          RecGadtC conNames types retType -> go (GadtC conNames (map removeName types) retType)++        mkClause :: Name -> [BangType] -> Q [Clause]+        mkClause conName types = return+          [Clause [ConP conName [] lpats, ConP conName [] rpats]+            (NormalB (AppE (ConE 'Just) (foldl AppE (ConE conName) args))) []]+          where+            (lpats, rpats, args) = unzip3+              [ case type_ of+                  VarT typeName+                    | typeName `elem` [scope, term] -> (VarP l, VarP r, TupE [Just (VarE l), Just (VarE r)])+                  _ -> (VarP l, WildP, VarE l)+              | (i, (_bang, type_)) <- zip [0..] types+              , let l = mkName ("l" ++ show i)+              , let r = mkName ("r" ++ show i)+              ]