packages feed

free-foil 0.6.0 → 0.6.1

raw patch · 16 files changed

+1522/−1078 lines, 16 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Control.Monad.Free.Foil: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.RelMonad Control.Monad.Foil.Internal.Name (Control.Monad.Free.Foil.AST binder sig)
- Control.Monad.Free.Foil: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.Sinkable (Control.Monad.Free.Foil.AST binder sig)
- Control.Monad.Free.Foil: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.Sinkable (Control.Monad.Free.Foil.ScopedAST binder sig)
- Control.Monad.Free.Foil: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.SinkableK (Control.Monad.Free.Foil.AST binder sig)
- Control.Monad.Free.Foil: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.SinkableK (Control.Monad.Free.Foil.ScopedAST binder sig)
- Control.Monad.Free.Foil: instance (Data.Bitraversable.Bitraversable sig, Data.ZipMatchK.Generic.ZipMatchK sig, Control.Monad.Foil.Internal.UnifiablePattern binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.AlphaEquiv (Control.Monad.Free.Foil.AST binder sig)
- Control.Monad.Free.Foil: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Control.DeepSeq.NFData (binder x y), forall (l :: Control.Monad.Foil.Internal.S). Control.DeepSeq.NFData (Control.Monad.Free.Foil.AST binder sig l)) => Control.DeepSeq.NFData (Control.Monad.Free.Foil.ScopedAST binder sig n)
- Control.Monad.Free.Foil: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Control.DeepSeq.NFData (binder x y), forall scope term. (Control.DeepSeq.NFData scope, Control.DeepSeq.NFData term) => Control.DeepSeq.NFData (sig scope term)) => Control.DeepSeq.NFData (Control.Monad.Free.Foil.AST binder sig n)
- Control.Monad.Free.Foil: instance Control.Monad.Foil.Internal.InjectName (Control.Monad.Free.Foil.AST binder sig)
- Control.Monad.Free.Foil: instance Data.Foldable.Foldable Control.Monad.Free.Foil.UnresolvedName
- Control.Monad.Free.Foil: instance Data.Traversable.Traversable Control.Monad.Free.Foil.UnresolvedName
- Control.Monad.Free.Foil: instance GHC.Base.Functor Control.Monad.Free.Foil.UnresolvedName
- Control.Monad.Free.Foil: instance GHC.Classes.Eq rawIdent => GHC.Classes.Eq (Control.Monad.Free.Foil.UnresolvedName rawIdent)
- Control.Monad.Free.Foil: instance GHC.Generics.Generic (Control.Monad.Free.Foil.AST binder sig n)
- Control.Monad.Free.Foil: instance GHC.Show.Show rawIdent => GHC.Show.Show (Control.Monad.Free.Foil.UnresolvedName rawIdent)
- Control.Monad.Free.Foil: instance Generics.Kind.GenericK (Control.Monad.Free.Foil.AST binder sig)
- Control.Monad.Free.Foil: instance Generics.Kind.GenericK (Control.Monad.Free.Foil.ScopedAST binder sig)
- Control.Monad.Free.Foil.Binary: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Data.Binary.Class.Binary (binder x y), forall (l :: Control.Monad.Foil.Internal.S). Data.Binary.Class.Binary (Control.Monad.Free.Foil.AST binder sig l)) => Data.Binary.Class.Binary (Control.Monad.Free.Foil.ScopedAST binder sig n)
- Control.Monad.Free.Foil.Binary: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Data.Binary.Class.Binary (binder x y), forall scope term. (Data.Binary.Class.Binary scope, Data.Binary.Class.Binary term) => Data.Binary.Class.Binary (sig scope term)) => Data.Binary.Class.Binary (Control.Monad.Free.Foil.AST binder sig n)
+ Control.Monad.Free.Foil: unresolvedNamesWith :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm (n :: S). (Distinct n, Bifoldable sig, Ord rawIdent, CoSinkable binder) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (forall (x :: S) z. Distinct x => Scope x -> Map rawIdent (Name x) -> rawPattern -> (forall (y :: S). DExt x y => binder x y -> Map rawIdent (Name y) -> z) -> z) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> Map rawIdent (AST binder sig n) -> rawTerm -> [UnresolvedName rawIdent]
+ Control.Monad.Free.Foil: unsafeEqASTIn :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil: unsafeEqScopedASTIn :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n) => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool
+ Control.Monad.Free.Foil.Binary: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Data.Binary.Class.Binary (binder x y), forall (l :: Control.Monad.Foil.Internal.S). Data.Binary.Class.Binary (Control.Monad.Free.Foil.Internal.AST binder sig l)) => Data.Binary.Class.Binary (Control.Monad.Free.Foil.Internal.ScopedAST binder sig n)
+ Control.Monad.Free.Foil.Binary: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Data.Binary.Class.Binary (binder x y), forall scope term. (Data.Binary.Class.Binary scope, Data.Binary.Class.Binary term) => Data.Binary.Class.Binary (sig scope term)) => Data.Binary.Class.Binary (Control.Monad.Free.Foil.Internal.AST binder sig n)
+ Control.Monad.Free.Foil.Internal: AlphaEquivLoops :: (forall (n :: S). Distinct n => Scope n -> AST binder sig n -> AST binder sig n -> Bool) -> (forall (n :: S). Distinct n => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool) -> (forall (m :: S) (l1 :: S) (l2 :: S). Distinct m => Int -> IntMap Int -> IntMap Int -> [(Int, Int)] -> Scope m -> AST binder sig l1 -> AST binder sig l2 -> Bool) -> (forall (n :: S) (n1 :: S) (n2 :: S). Distinct n => Int -> IntMap Int -> IntMap Int -> Scope n -> AST binder sig n1 -> AST binder sig n2 -> Bool) -> (forall (n :: S) (n1 :: S) (n2 :: S). Distinct n => Int -> IntMap Int -> IntMap Int -> Scope n -> ScopedAST binder sig n1 -> ScopedAST binder sig n2 -> Bool) -> AlphaEquivLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type)
+ Control.Monad.Free.Foil.Internal: EqInLoops :: (forall (n :: S). Distinct n => Scope n -> AST binder sig n -> AST binder sig n -> Bool) -> (forall (n :: S). Distinct n => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool) -> EqInLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type)
+ Control.Monad.Free.Foil.Internal: EqLoops :: (forall (n :: S) (l :: S). (Distinct n, Distinct l) => AST binder sig n -> AST binder sig l -> Bool) -> (forall (n :: S) (l :: S). (Distinct n, Distinct l) => ScopedAST binder sig n -> ScopedAST binder sig l -> Bool) -> EqLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type)
+ Control.Monad.Free.Foil.Internal: UnresolvedName :: rawIdent -> [rawIdent] -> UnresolvedName rawIdent
+ Control.Monad.Free.Foil.Internal: [Node] :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). sig (ScopedAST binder sig n) (AST binder sig n) -> AST binder sig n
+ Control.Monad.Free.Foil.Internal: [ScopedAST] :: forall (binder :: S -> S -> Type) (n :: S) (l :: S) (sig :: Type -> Type -> Type). binder n l -> AST binder sig l -> ScopedAST binder sig n
+ Control.Monad.Free.Foil.Internal: [Var] :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type). {-# UNPACK #-} !Name n -> AST binder sig n
+ Control.Monad.Free.Foil.Internal: [aeAlphaEquivEnv] :: AlphaEquivLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S) (n1 :: S) (n2 :: S). Distinct n => Int -> IntMap Int -> IntMap Int -> Scope n -> AST binder sig n1 -> AST binder sig n2 -> Bool
+ Control.Monad.Free.Foil.Internal: [aeAlphaEquivScopedEnv] :: AlphaEquivLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S) (n1 :: S) (n2 :: S). Distinct n => Int -> IntMap Int -> IntMap Int -> Scope n -> ScopedAST binder sig n1 -> ScopedAST binder sig n2 -> Bool
+ Control.Monad.Free.Foil.Internal: [aeAlphaEquivScoped] :: AlphaEquivLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S). Distinct n => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: [aeAlphaEquiv] :: AlphaEquivLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S). Distinct n => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: [aeBindPairs] :: AlphaEquivLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (m :: S) (l1 :: S) (l2 :: S). Distinct m => Int -> IntMap Int -> IntMap Int -> [(Int, Int)] -> Scope m -> AST binder sig l1 -> AST binder sig l2 -> Bool
+ Control.Monad.Free.Foil.Internal: [eqASTIn] :: EqInLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S). Distinct n => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: [eqAST] :: EqLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S) (l :: S). (Distinct n, Distinct l) => AST binder sig n -> AST binder sig l -> Bool
+ Control.Monad.Free.Foil.Internal: [eqScopedASTIn] :: EqInLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S). Distinct n => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: [eqScopedAST] :: EqLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) -> forall (n :: S) (l :: S). (Distinct n, Distinct l) => ScopedAST binder sig n -> ScopedAST binder sig l -> Bool
+ Control.Monad.Free.Foil.Internal: [unresolvedIdent] :: UnresolvedName rawIdent -> rawIdent
+ Control.Monad.Free.Foil.Internal: [unresolvedInScope] :: UnresolvedName rawIdent -> [rawIdent]
+ Control.Monad.Free.Foil.Internal: alphaEquiv :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: alphaEquivEnv :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (n1 :: S) (n2 :: S). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Int -> IntMap Int -> IntMap Int -> Scope n -> AST binder sig n1 -> AST binder sig n2 -> Bool
+ Control.Monad.Free.Foil.Internal: alphaEquivLoops :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type). (ZipMatchK sig, UnifiablePattern binder) => AlphaEquivLoops binder sig
+ Control.Monad.Free.Foil.Internal: alphaEquivRefreshed :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (Bifunctor sig, ZipMatchK sig, Distinct n, UnifiablePattern binder, SinkableK binder) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: alphaEquivScoped :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: alphaEquivScopedEnv :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (n1 :: S) (n2 :: S). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Int -> IntMap Int -> IntMap Int -> Scope n -> ScopedAST binder sig n1 -> ScopedAST binder sig n2 -> Bool
+ Control.Monad.Free.Foil.Internal: bindPairs :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (m :: S) (l1 :: S) (l2 :: S). (ZipMatchK sig, Distinct m, UnifiablePattern binder) => Int -> IntMap Int -> IntMap Int -> [(Int, Int)] -> Scope m -> AST binder sig l1 -> AST binder sig l2 -> Bool
+ Control.Monad.Free.Foil.Internal: convertFromAST :: forall sig rawPattern rawScopedTerm rawTerm rawIdent binder (n :: S). Bifunctor sig => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm) -> (rawIdent -> rawTerm) -> (forall (x :: S) (y :: S). () => binder x y -> rawPattern) -> (rawTerm -> rawScopedTerm) -> (Int -> rawIdent) -> AST binder sig n -> rawTerm
+ Control.Monad.Free.Foil.Internal: convertFromASTWith :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm (n :: S). (Bifunctor sig, Distinct n, CoSinkable binder) => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm) -> (rawIdent -> rawTerm) -> (forall (x :: S) (y :: S). () => binder x y -> rawPattern) -> (rawTerm -> rawScopedTerm) -> (Name n -> rawIdent) -> (Int -> rawIdent) -> AST binder sig n -> rawTerm
+ Control.Monad.Free.Foil.Internal: convertFromScopedAST :: forall sig rawPattern rawScopedTerm rawTerm rawIdent binder (n :: S). Bifunctor sig => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm) -> (rawIdent -> rawTerm) -> (forall (x :: S) (y :: S). () => binder x y -> rawPattern) -> (rawTerm -> rawScopedTerm) -> (Int -> rawIdent) -> ScopedAST binder sig n -> (rawPattern, rawScopedTerm)
+ Control.Monad.Free.Foil.Internal: data AST (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S)
+ Control.Monad.Free.Foil.Internal: data AlphaEquivLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type)
+ Control.Monad.Free.Foil.Internal: data EqInLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type)
+ Control.Monad.Free.Foil.Internal: data EqLoops (binder :: S -> S -> Type) (sig :: Type -> Type -> Type)
+ Control.Monad.Free.Foil.Internal: data ScopedAST (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S)
+ Control.Monad.Free.Foil.Internal: data UnresolvedName rawIdent
+ Control.Monad.Free.Foil.Internal: foldAST :: forall binder sig r s (n :: S). Bifunctor sig => (forall (m :: S). () => Name m -> r m) -> (forall (m :: S) (l :: S). () => binder m l -> r l -> s m) -> (forall (m :: S). () => sig (s m) (r m) -> r m) -> AST binder sig n -> r n
+ Control.Monad.Free.Foil.Internal: freeVarsOf :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type). (Distinct n, CoSinkable binder, Bifoldable sig) => AST binder sig n -> [Name n]
+ Control.Monad.Free.Foil.Internal: freeVarsOfScopedAST :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type). (Distinct n, CoSinkable binder, Bifoldable sig) => ScopedAST binder sig n -> [Name n]
+ Control.Monad.Free.Foil.Internal: hoistAST :: forall sig sig' (binder :: S -> S -> Type) (n :: S). Bifunctor sig => (forall a b. () => sig a b -> sig' a b) -> AST binder sig n -> AST binder sig' n
+ Control.Monad.Free.Foil.Internal: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.RelMonad Control.Monad.Foil.Internal.Name (Control.Monad.Free.Foil.Internal.AST binder sig)
+ Control.Monad.Free.Foil.Internal: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.Sinkable (Control.Monad.Free.Foil.Internal.AST binder sig)
+ Control.Monad.Free.Foil.Internal: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.Sinkable (Control.Monad.Free.Foil.Internal.ScopedAST binder sig)
+ Control.Monad.Free.Foil.Internal: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.SinkableK (Control.Monad.Free.Foil.Internal.AST binder sig)
+ Control.Monad.Free.Foil.Internal: instance (Data.Bifunctor.Bifunctor sig, Control.Monad.Foil.Internal.CoSinkable binder, Control.Monad.Foil.Internal.SinkableK binder) => Control.Monad.Foil.Internal.SinkableK (Control.Monad.Free.Foil.Internal.ScopedAST binder sig)
+ Control.Monad.Free.Foil.Internal: instance (Data.ZipMatchK.Generic.ZipMatchK sig, Control.Monad.Foil.Internal.UnifiablePattern binder) => Control.Monad.Foil.Internal.AlphaEquiv (Control.Monad.Free.Foil.Internal.AST binder sig)
+ Control.Monad.Free.Foil.Internal: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Control.DeepSeq.NFData (binder x y), forall (l :: Control.Monad.Foil.Internal.S). Control.DeepSeq.NFData (Control.Monad.Free.Foil.Internal.AST binder sig l)) => Control.DeepSeq.NFData (Control.Monad.Free.Foil.Internal.ScopedAST binder sig n)
+ Control.Monad.Free.Foil.Internal: instance (forall (x :: Control.Monad.Foil.Internal.S) (y :: Control.Monad.Foil.Internal.S). Control.DeepSeq.NFData (binder x y), forall scope term. (Control.DeepSeq.NFData scope, Control.DeepSeq.NFData term) => Control.DeepSeq.NFData (sig scope term)) => Control.DeepSeq.NFData (Control.Monad.Free.Foil.Internal.AST binder sig n)
+ Control.Monad.Free.Foil.Internal: instance Control.Monad.Foil.Internal.InjectName (Control.Monad.Free.Foil.Internal.AST binder sig)
+ Control.Monad.Free.Foil.Internal: instance Data.Foldable.Foldable Control.Monad.Free.Foil.Internal.UnresolvedName
+ Control.Monad.Free.Foil.Internal: instance Data.Traversable.Traversable Control.Monad.Free.Foil.Internal.UnresolvedName
+ Control.Monad.Free.Foil.Internal: instance GHC.Base.Functor Control.Monad.Free.Foil.Internal.UnresolvedName
+ Control.Monad.Free.Foil.Internal: instance GHC.Classes.Eq rawIdent => GHC.Classes.Eq (Control.Monad.Free.Foil.Internal.UnresolvedName rawIdent)
+ Control.Monad.Free.Foil.Internal: instance GHC.Generics.Generic (Control.Monad.Free.Foil.Internal.AST binder sig n)
+ Control.Monad.Free.Foil.Internal: instance GHC.Show.Show rawIdent => GHC.Show.Show (Control.Monad.Free.Foil.Internal.UnresolvedName rawIdent)
+ Control.Monad.Free.Foil.Internal: instance Generics.Kind.GenericK (Control.Monad.Free.Foil.Internal.AST binder sig)
+ Control.Monad.Free.Foil.Internal: instance Generics.Kind.GenericK (Control.Monad.Free.Foil.Internal.ScopedAST binder sig)
+ Control.Monad.Free.Foil.Internal: refreshAST :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (Bifunctor sig, Distinct n, CoSinkable binder, SinkableK binder) => Scope n -> AST binder sig n -> AST binder sig n
+ Control.Monad.Free.Foil.Internal: refreshScopedAST :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (Bifunctor sig, Distinct n, CoSinkable binder, SinkableK binder) => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n
+ Control.Monad.Free.Foil.Internal: renamedId :: forall (n :: S) (a :: S) (b :: S). (NameBinder n a -> NameBinder n b) -> Name a -> Int
+ Control.Monad.Free.Foil.Internal: substitute :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (o :: S) (i :: S). (Bifunctor sig, Distinct o, CoSinkable binder, SinkableK binder) => Scope o -> Substitution (AST binder sig) i o -> AST binder sig i -> AST binder sig o
+ Control.Monad.Free.Foil.Internal: substitutePattern :: forall (sig :: Type -> Type -> Type) (o :: S) binder' (binder :: S -> S -> Type) (n :: S) (i :: S). (Bifunctor sig, Distinct o, CoSinkable binder', CoSinkable binder, SinkableK binder) => Scope o -> Substitution (AST binder sig) n o -> binder' n i -> [AST binder sig o] -> AST binder sig i -> AST binder sig o
+ Control.Monad.Free.Foil.Internal: substituteRefreshed :: forall (sig :: Type -> Type -> Type) (o :: S) (binder :: S -> S -> Type) (i :: S). (Bifunctor sig, Distinct o, CoSinkable binder, SinkableK binder) => Scope o -> Substitution (AST binder sig) i o -> AST binder sig i -> AST binder sig o
+ Control.Monad.Free.Foil.Internal: supportOf :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type). (Distinct n, CoSinkable binder, Bifoldable sig) => AST binder sig n -> NameSet n
+ Control.Monad.Free.Foil.Internal: supportOfScopedAST :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type). (Distinct n, CoSinkable binder, Bifoldable sig) => ScopedAST binder sig n -> NameSet n
+ Control.Monad.Free.Foil.Internal: tryConvertToAST :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm (n :: S). (Distinct n, Bitraversable sig, Ord rawIdent, CoSinkable binder, SinkableK binder) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (forall (x :: S) z. Distinct x => Scope x -> Map rawIdent (Name x) -> rawPattern -> (forall (y :: S). DExt x y => binder x y -> Map rawIdent (Name y) -> z) -> z) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> rawTerm -> Either (UnresolvedName rawIdent) (AST binder sig n)
+ Control.Monad.Free.Foil.Internal: tryConvertToASTWith :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm (n :: S). (Distinct n, Bitraversable sig, Ord rawIdent, CoSinkable binder, SinkableK binder) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (forall (x :: S) z. Distinct x => Scope x -> Map rawIdent (Name x) -> rawPattern -> (forall (y :: S). DExt x y => binder x y -> Map rawIdent (Name y) -> z) -> z) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> Map rawIdent (AST binder sig n) -> rawTerm -> Either (UnresolvedName rawIdent) (AST binder sig n)
+ Control.Monad.Free.Foil.Internal: unresolvedNames :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm (n :: S). (Distinct n, Bifoldable sig, Ord rawIdent, CoSinkable binder) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (forall (x :: S) z. Distinct x => Scope x -> Map rawIdent (Name x) -> rawPattern -> (forall (y :: S). DExt x y => binder x y -> Map rawIdent (Name y) -> z) -> z) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> rawTerm -> [UnresolvedName rawIdent]
+ Control.Monad.Free.Foil.Internal: unresolvedNamesWith :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm (n :: S). (Distinct n, Bifoldable sig, Ord rawIdent, CoSinkable binder) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (forall (x :: S) z. Distinct x => Scope x -> Map rawIdent (Name x) -> rawPattern -> (forall (y :: S). DExt x y => binder x y -> Map rawIdent (Name y) -> z) -> z) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> Map rawIdent (AST binder sig n) -> rawTerm -> [UnresolvedName rawIdent]
+ Control.Monad.Free.Foil.Internal: unsafeConvertToAST :: forall (n :: S) sig rawIdent binder rawTerm rawPattern rawScopedTerm. (Distinct n, Bifunctor sig, Ord rawIdent, CoSinkable binder) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (forall (x :: S) z. Distinct x => Scope x -> Map rawIdent (Name x) -> rawPattern -> (forall (y :: S). DExt x y => binder x y -> Map rawIdent (Name y) -> z) -> z) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> rawTerm -> AST binder sig n
+ Control.Monad.Free.Foil.Internal: unsafeConvertToScopedAST :: forall (n :: S) sig rawIdent binder rawTerm rawPattern rawScopedTerm. (Distinct n, Bifunctor sig, Ord rawIdent, CoSinkable binder) => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm)) -> (forall (x :: S) z. Distinct x => Scope x -> Map rawIdent (Name x) -> rawPattern -> (forall (y :: S). DExt x y => binder x y -> Map rawIdent (Name y) -> z) -> z) -> (rawScopedTerm -> rawTerm) -> Scope n -> Map rawIdent (Name n) -> (rawPattern, rawScopedTerm) -> ScopedAST binder sig n
+ Control.Monad.Free.Foil.Internal: unsafeEqAST :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (l :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n, Distinct l) => AST binder sig n -> AST binder sig l -> Bool
+ Control.Monad.Free.Foil.Internal: unsafeEqASTIn :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: unsafeEqInLoops :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type). (ZipMatchK sig, UnifiablePattern binder) => EqInLoops binder sig
+ Control.Monad.Free.Foil.Internal: unsafeEqLoops :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type). (ZipMatchK sig, UnifiablePattern binder) => EqLoops binder sig
+ Control.Monad.Free.Foil.Internal: unsafeEqScopedAST :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (l :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n, Distinct l) => ScopedAST binder sig n -> ScopedAST binder sig l -> Bool
+ Control.Monad.Free.Foil.Internal: unsafeEqScopedASTIn :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n) => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool
+ Control.Monad.Free.Foil.Internal: unsinkAST :: forall (n :: S) (l :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type). (DExt n l, CoSinkable binder, Bifoldable sig) => Scope n -> AST binder sig l -> Maybe (AST binder sig n)
+ Control.Monad.Free.Foil.Internal: withRelevantScope :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) r. (Distinct n, CoSinkable binder, Bifoldable sig) => AST binder sig n -> (forall (m :: S). (Ext m n, Distinct m) => Scope m -> AST binder sig m -> r) -> r
- Control.Monad.Foil: nameSetSubsetOfScope :: forall (l :: S) (n :: S). NameSet l -> Scope n -> Bool
+ Control.Monad.Foil: nameSetSubsetOfScope :: forall (n :: S) (l :: S). Ext n l => NameSet l -> Scope n -> Bool
- Control.Monad.Foil.Internal: nameSetSubsetOfScope :: forall (l :: S) (n :: S). NameSet l -> Scope n -> Bool
+ Control.Monad.Foil.Internal: nameSetSubsetOfScope :: forall (n :: S) (l :: S). Ext n l => NameSet l -> Scope n -> Bool
- Control.Monad.Free.Foil: alphaEquiv :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (Bitraversable sig, ZipMatchK sig, Distinct n, UnifiablePattern binder, SinkableK binder) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil: alphaEquiv :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
- Control.Monad.Free.Foil: alphaEquivEnv :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (n1 :: S) (n2 :: S). (Bitraversable sig, ZipMatchK sig, Distinct n, UnifiablePattern binder, SinkableK binder) => Int -> IntMap Int -> IntMap Int -> Scope n -> AST binder sig n1 -> AST binder sig n2 -> Bool
+ Control.Monad.Free.Foil: alphaEquivEnv :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (n1 :: S) (n2 :: S). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Int -> IntMap Int -> IntMap Int -> Scope n -> AST binder sig n1 -> AST binder sig n2 -> Bool
- Control.Monad.Free.Foil: alphaEquivRefreshed :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (Bitraversable sig, ZipMatchK sig, Distinct n, UnifiablePattern binder, SinkableK binder) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
+ Control.Monad.Free.Foil: alphaEquivRefreshed :: forall (sig :: Type -> Type -> Type) (n :: S) (binder :: S -> S -> Type). (Bifunctor sig, ZipMatchK sig, Distinct n, UnifiablePattern binder, SinkableK binder) => Scope n -> AST binder sig n -> AST binder sig n -> Bool
- Control.Monad.Free.Foil: alphaEquivScoped :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (Bitraversable sig, ZipMatchK sig, Distinct n, UnifiablePattern binder, SinkableK binder) => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool
+ Control.Monad.Free.Foil: alphaEquivScoped :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool
- Control.Monad.Free.Foil: alphaEquivScopedEnv :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (n1 :: S) (n2 :: S). (Bitraversable sig, ZipMatchK sig, Distinct n, UnifiablePattern binder, SinkableK binder) => Int -> IntMap Int -> IntMap Int -> Scope n -> ScopedAST binder sig n1 -> ScopedAST binder sig n2 -> Bool
+ Control.Monad.Free.Foil: alphaEquivScopedEnv :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (n1 :: S) (n2 :: S). (ZipMatchK sig, Distinct n, UnifiablePattern binder) => Int -> IntMap Int -> IntMap Int -> Scope n -> ScopedAST binder sig n1 -> ScopedAST binder sig n2 -> Bool
- Control.Monad.Free.Foil: bindPairs :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (m :: S) (l1 :: S) (l2 :: S). (Bitraversable sig, ZipMatchK sig, Distinct m, UnifiablePattern binder, SinkableK binder) => Int -> IntMap Int -> IntMap Int -> [(Int, Int)] -> Scope m -> AST binder sig l1 -> AST binder sig l2 -> Bool
+ Control.Monad.Free.Foil: bindPairs :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (m :: S) (l1 :: S) (l2 :: S). (ZipMatchK sig, Distinct m, UnifiablePattern binder) => Int -> IntMap Int -> IntMap Int -> [(Int, Int)] -> Scope m -> AST binder sig l1 -> AST binder sig l2 -> Bool
- Control.Monad.Free.Foil: substitute :: forall (sig :: Type -> Type -> Type) (o :: S) (binder :: S -> S -> Type) (i :: S). (Bifunctor sig, Distinct o, CoSinkable binder, SinkableK binder) => Scope o -> Substitution (AST binder sig) i o -> AST binder sig i -> AST binder sig o
+ Control.Monad.Free.Foil: substitute :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (o :: S) (i :: S). (Bifunctor sig, Distinct o, CoSinkable binder, SinkableK binder) => Scope o -> Substitution (AST binder sig) i o -> AST binder sig i -> AST binder sig o
- Control.Monad.Free.Foil: unsafeEqAST :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (l :: S). (Bitraversable sig, ZipMatchK sig, UnifiablePattern binder, Distinct n, Distinct l) => AST binder sig n -> AST binder sig l -> Bool
+ Control.Monad.Free.Foil: unsafeEqAST :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (l :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n, Distinct l) => AST binder sig n -> AST binder sig l -> Bool
- Control.Monad.Free.Foil: unsafeEqScopedAST :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (l :: S). (Bitraversable sig, ZipMatchK sig, UnifiablePattern binder, Distinct n, Distinct l) => ScopedAST binder sig n -> ScopedAST binder sig l -> Bool
+ Control.Monad.Free.Foil: unsafeEqScopedAST :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S) (l :: S). (ZipMatchK sig, UnifiablePattern binder, Distinct n, Distinct l) => ScopedAST binder sig n -> ScopedAST binder sig l -> Bool
- Control.Monad.Free.Foil: unsinkAST :: forall (l :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S). (Distinct l, CoSinkable binder, Bifoldable sig) => Scope n -> AST binder sig l -> Maybe (AST binder sig n)
+ Control.Monad.Free.Foil: unsinkAST :: forall (n :: S) (l :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type). (DExt n l, CoSinkable binder, Bifoldable sig) => Scope n -> AST binder sig l -> Maybe (AST binder sig n)
- Data.ZipMatchK: zipMatch1 :: (Traversable f, ZipMatchK f) => f a -> f a' -> Maybe (f (a, a'))
+ Data.ZipMatchK: zipMatch1 :: ZipMatchK f => f a -> f a' -> Maybe (f (a, a'))
- Data.ZipMatchK: zipMatch2 :: (Bitraversable f, ZipMatchK f) => f a b -> f a' b' -> Maybe (f (a, a') (b, b'))
+ Data.ZipMatchK: zipMatch2 :: ZipMatchK f => f a b -> f a' b' -> Maybe (f (a, a') (b, b'))
- Data.ZipMatchK: zipMatchWith1 :: (Traversable f, ZipMatchK f) => (a -> a' -> Maybe a'') -> f a -> f a' -> Maybe (f a'')
+ Data.ZipMatchK: zipMatchWith1 :: ZipMatchK f => (a -> a' -> Maybe a'') -> f a -> f a' -> Maybe (f a'')
- Data.ZipMatchK: zipMatchWith2 :: (Bitraversable f, ZipMatchK f) => (a -> a' -> Maybe a'') -> (b -> b' -> Maybe b'') -> f a b -> f a' b' -> Maybe (f a'' b'')
+ Data.ZipMatchK: zipMatchWith2 :: ZipMatchK f => (a -> a' -> Maybe a'') -> (b -> b' -> Maybe b'') -> f a b -> f a' b' -> Maybe (f a'' b'')

Files

ChangeLog.md view
@@ -1,5 +1,36 @@ # CHANGELOG for `free-foil` +# 0.6.1 — 2026-10-27++This release lets `substitute`, `alphaEquiv` and `unsafeEqAST` specialise in client code, and fixes problems found by an audit of the documentation. One of the fixes changes the types of `unsinkAST` and `nameSetSubsetOfScope`, described first.++Breaking changes:++- `unsinkAST` requires `DExt n l` instead of `Distinct l`, and `nameSetSubsetOfScope` requires `Ext n l`: the scope `l` of the term (or set) must extend the scope `n` it is restricted to. Both compare raw identifiers, which identify a name only within one chain of scope extensions, so with unrelated scopes the answer was meaningless (see [#127](https://github.com/fizruk/free-foil/pull/127)).+  - To migrate, bring `Ext n l` into scope where it is missing, e.g. with `assertExt` on the binder that leads from `n` to `l`, or from `withFresh` and `withThinnedNameBinderList`, which provide it.++Changed:++- `zipMatchWith1`, `zipMatch1`, `zipMatchWith2` and `zipMatch2` no longer require `Traversable` (or `Bitraversable`). `alphaEquiv`, `unsafeEqAST` and the functions behind them no longer require `Bitraversable` or `SinkableK`, and `alphaEquivRefreshed` requires `Bifunctor` instead of `Bitraversable`. None of them used these instances (see [#125](https://github.com/fizruk/free-foil/pull/125)).++Fixes:++- `alphaEquivRefreshed` compares patterns with `unifyPatternsIn`, as `alphaEquiv` does (through the new `unsafeEqASTIn`). It compared them with `unifyPatterns`, which compares binders only, so it accepted terms whose binders differ in a payload (such as a type annotation) that `alphaEquiv` rejects (see [#127](https://github.com/fizruk/free-foil/pull/127)).++Performance:++- `substitute`, `alphaEquiv`, `unsafeEqAST` and the functions behind them are `INLINE` wrappers around local loops, which take the instances from the wrapper. Inlined at a known signature, the loops use its instances directly. Before, they looked the instances up at every node, and a `SPECIALIZE` pragma in a client did not help: GHC split each function into a wrapper and a worker, since some of its dictionaries (such as `Distinct`) are never used at runtime, and the pragma reached only the wrapper. On the library's benchmarks, α-equivalence takes 0.54–0.91 of the time and normalisation 0.58–0.85 (see [#125](https://github.com/fizruk/free-foil/pull/125)).++New:++- `unsafeEqASTIn` and `unsafeEqScopedASTIn` compare two terms in one scope, like `unsafeEqAST`, but compare patterns with `unifyPatternsIn`, so the payloads of patterns are compared as well (see [#127](https://github.com/fizruk/free-foil/pull/127)).+- `unresolvedNamesWith` is `unresolvedNames` for the arguments of `tryConvertToASTWith`: it also takes the table of constants, so that the constants no longer count as unresolved (see [#127](https://github.com/fizruk/free-foil/pull/127)).+- `Control.Monad.Free.Foil.Internal` holds the implementation of `Control.Monad.Free.Foil`, which now has an export list. The loops behind α-equivalence, `unsafeEqAST` and `unsafeEqASTIn` (`AlphaEquivLoops`, `alphaEquivLoops`, `EqLoops`, `unsafeEqLoops`, `EqInLoops`, `unsafeEqInLoops`) are exported only from the internal module (see [#125](https://github.com/fizruk/free-foil/pull/125), [#127](https://github.com/fizruk/free-foil/pull/127)).++Documentation:++- Doc comments and README passages that the code contradicted are corrected, among them the equation given for `substituteRefreshed` and the `zipMatchK` example. Haddock links written through a module alias (such as `'Foil.sink'`) resolve, where in 0.6.0 they pointed to a nonexistent page (see [#126](https://github.com/fizruk/free-foil/pull/126)).+ # 0.6.0 — 2026-10-26  This release lets `mkFreeFoil` generate most instances of a signature, and adds folds that traverse terms without rebuilding them. Several breaking changes come with it, described first.
free-foil.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.37.0.+-- This file has been generated from package.yaml by hpack version 0.39.1. -- -- see: https://github.com/sol/hpack  name:           free-foil-version:        0.6.0+version:        0.6.1 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@@ -48,6 +48,7 @@       Control.Monad.Free.Foil.Binary       Control.Monad.Free.Foil.Binary.TH       Control.Monad.Free.Foil.Example+      Control.Monad.Free.Foil.Internal       Control.Monad.Free.Foil.TH       Control.Monad.Free.Foil.TH.Convert       Control.Monad.Free.Foil.TH.MkFreeFoil@@ -129,6 +130,7 @@       Control.Monad.Free.Foil.TH.MkFreeFoilSpec.PatternsSyntax       Control.Monad.Free.Foil.TH.MkFreeFoilSpec.Syntax       Control.Monad.Free.Foil.TH.PatternTypesSpec+      Control.Monad.Free.Foil.UnresolvedNamesSpec       Data.ZipMatchK.SumProductSpec       Data.ZipMatchK.THSpec       SpecHook
src/Control/Monad/Foil.hs view
@@ -1,8 +1,8 @@ -- | Main definitions of the foil that can be -- reused for specific implementations. ----- The original description of this approach--- is described in the IFL 2022 paper by Maclaurin, Radul, and Paszke+-- This approach was originally described+-- in the IFL 2022 paper by Maclaurin, Radul, and Paszke -- [«The Foil: Capture-Avoiding Substitution With No Sharp Edges»](https://doi.org/10.1145/3587216.3587224). -- This module also introduces 'CoSinkable' class, -- generalizing handling of patterns, as described in@@ -60,12 +60,6 @@   SinkableK(..),   Sinkable(..),   CoSinkable(..),-  -- ** Transporting a pattern's payloads-  PatternTransport,-  verbatimTransport,-  transportUnderBinder,-  transportPayload,-  transportName,   HasNameBinders(getNameBinders),   sink,   sink1,@@ -76,6 +70,12 @@   composeNameBinderRenamings,   fromNameBinderRenaming,   extendRenamingNameBinder,+  -- ** Transporting a pattern's payloads+  PatternTransport,+  verbatimTransport,+  transportUnderBinder,+  transportPayload,+  transportName,   -- * Safe substitutions   Substitution,   lookupSubst,
src/Control/Monad/Foil/Internal.hs view
@@ -680,8 +680,12 @@ -- place a restriction is paid for: compare a term\'s support against the scope -- it is to be restricted to. --+-- The scope @l@ of the set must extend @n@, since names are compared by their+-- raw identifiers, which identify a name only within one chain of scope+-- extensions.+-- -- @since 0.4.0-nameSetSubsetOfScope :: NameSet l -> Scope n -> Bool+nameSetSubsetOfScope :: Ext n l => NameSet l -> Scope n -> Bool nameSetSubsetOfScope (UnsafeNameSet names) (UnsafeScope scope) =   names `IntSet.isSubsetOf` scope @@ -1215,9 +1219,9 @@   -- pattern that carries them does: 'alphaEquivIn' asks for a 'Scope'.   --   -- Note that the verdict speaks about binders, so an instance comparing-  -- payloads has to apply the renaming the verdict prescribes before it-  -- compares them, exactly as 'Control.Monad.Free.Foil.alphaEquivScoped'-  -- applies it to the body of a scoped term. Two telescopes @(A : 𝕌) (x : A)@+  -- payloads has to take the renaming the verdict prescribes into account when+  -- it compares them, as 'Control.Monad.Free.Foil.alphaEquivScoped' does for+  -- the body of a scoped term. Two telescopes @(A : 𝕌) (x : A)@   -- and @(B : 𝕌) (y : B)@ are α-equivalent, and their second payloads are only   -- equal once the first binders have been identified.   --@@ -2497,7 +2501,7 @@         cont (putBackRenamingK @_ @i rename' irename) (Field (unsafeCoerce x')) -- unsafeCoerce?  -- | A field indexed by the innermost scope variable only, such as the body of--- 'Control.Monad.Free.Foil.ScopedAST'. Under a binder the traversal holds one+-- t'Control.Monad.Free.Foil.ScopedAST'. Under a binder the traversal holds one -- renaming per scope variable, so the field takes the innermost one, as -- @t'Field' ('Kon' f ':@:' 'Var' i)@ does. instance SinkableK (f a) => GSinkableK (Field (Kon f :@: Kon a :@: Var0)) where
src/Control/Monad/Foil/TH/MkFoilData.hs view
@@ -116,7 +116,7 @@         toPatternConParams i p (param@(bang_, type_) : conParams) =           case type_ of             -- if the current component is a variable identifier-            -- then treat it as a single name binder (see 'Foil.NameBinder')+            -- then treat it as a single name binder (see t'Control.Monad.Foil.NameBinder')             PeelConT tyName _tyParams | tyName == nameT -> do               l <- newName ("n" <> show i)               let type' = AppT (AppT (ConT ''Foil.NameBinder) (VarT p)) (VarT l)
src/Control/Monad/Foil/TH/MkInstancesFoil.hs view
@@ -14,7 +14,7 @@ import qualified Control.Monad.Foil         as Foil import           Control.Monad.Foil.TH.Util --- | Generate 'Foil.Sinkable' and 'Foil.CoSinkable' instances.+-- | Generate t'Control.Monad.Foil.Sinkable' and t'Control.Monad.Foil.CoSinkable' instances. -- -- @since 0.0.1 mkInstancesFoil@@ -83,7 +83,7 @@           where             xi = mkName ("x" ++ show i) --- | Generate the 'Foil.CoSinkable' instance of the pattern type that+-- | Generate the t'Control.Monad.Foil.CoSinkable' instance of the pattern type that -- 'Control.Monad.Foil.TH.MkFoilData.mkFoilPattern' generates from a raw -- pattern type, given the raw types of identifiers and patterns. -- 'deriveCoSinkable' takes the scope-safe pattern type itself.@@ -143,7 +143,7 @@             xi = mkName ("x" ++ show i)      -- The binders of the binding fields, in order, without going through-    -- 'Foil.withPattern'.+    -- 'Control.Monad.Foil.withPattern'.     clauseNameBinderListOf :: Con -> Clause     clauseNameBinderListOf (NormalC conName params) =       Clause [ConP foilConName [] conParamPatterns] (NormalB body) []@@ -201,7 +201,7 @@           where             xi = mkName ("x" ++ show i) --- | Generate the 'Foil.CoSinkable' instance of a scope-safe pattern type.+-- | Generate the t'Control.Monad.Foil.CoSinkable' instance of a scope-safe pattern type. -- 'mkCoSinkableFoilPattern' takes the raw pattern type instead. -- -- > data Pat (n :: S) (l :: S) where@@ -210,9 +210,9 @@ -- > -- > deriveCoSinkable ''Pat ----- A field is a 'Foil.NameBinder', a nested pattern, or a payload that+-- A field is a t'Control.Monad.Foil.NameBinder', a nested pattern, or a payload that -- mentions no scope. A payload indexed by a scope needs a hand-written--- instance (see 'Foil.transportPayload').+-- instance (see 'Control.Monad.Foil.transportPayload'). -- -- @since 0.6.0 deriveCoSinkable :: Name -> Q [Dec]@@ -229,7 +229,7 @@       coSinkClauses <- mapM patternCoSinkabilityProofClause fields       withPatClauses <- mapM patternWithPatternClause fields       listClauses <- mapM patternNameBinderListOfClause fields-      -- A pattern that nests itself has a recursive 'Foil.withPattern', which+      -- A pattern that nests itself has a recursive 'Control.Monad.Foil.withPattern', which       -- cannot inline.       let recursive = or [ h == ConT patternT | (_, sorts) <- fields, PatternNested h <- sorts ]           inline = [ PragmaD (InlineP 'Foil.withPattern Inline FunLike AllPhases) | not recursive ]@@ -243,8 +243,8 @@  -- | How a field of a pattern constructor takes part in its instance. data PatternField-  = PatternBinder        -- ^ A 'Foil.NameBinder'.-  | PatternNested Type   -- ^ A nested pattern, with its own 'Foil.CoSinkable'; its type's head.+  = PatternBinder        -- ^ A t'Control.Monad.Foil.NameBinder'.+  | PatternNested Type   -- ^ A nested pattern, with its own t'Control.Monad.Foil.CoSinkable'; its type's head.   | PatternPayload       -- ^ A field that mentions no scope.  -- | The constructors of a pattern type, each with the sorts of its fields.@@ -305,7 +305,7 @@   PatternPayload -> False   _              -> True --- | One 'Foil.coSinkabilityProof' clause: the binding fields are renamed in+-- | One 'Control.Monad.Foil.coSinkabilityProof' clause: the binding fields are renamed in -- turn, each with the renaming the previous one extended. patternCoSinkabilityProofClause :: (Name, [PatternField]) -> Q Clause patternCoSinkabilityProofClause (conName, sorts) = do@@ -323,10 +323,10 @@   body <- go rename (zip sorts xs) id   return (Clause [VarP rename, ConP conName [] (map VarP xs), VarP cont] (NormalB body) []) --- | One 'Foil.withPattern' clause, in the form of+-- | One 'Control.Monad.Foil.withPattern' clause, in the form of -- 'Control.Monad.Free.Foil.TH.MkFreeFoil.mkFreeFoil': a binder field goes -- through @withBinder@ and extends the scope for the fields to its right, a--- nested pattern through its own 'Foil.withPattern', and the results compose+-- nested pattern through its own 'Control.Monad.Foil.withPattern', and the results compose -- left to right. patternWithPatternClause :: (Name, [PatternField]) -> Q Clause patternWithPatternClause (conName, sorts) = do@@ -368,13 +368,13 @@     [VarP withBinder, VarP unit_, VarP comp_, VarP scope, ConP conName [] (map VarP xs), VarP cont]     (NormalB body) []) --- | One 'Foil.nameBinderListOf' clause: the binding fields, left to right.+-- | One 'Control.Monad.Foil.nameBinderListOf' clause: the binding fields, left to right. patternNameBinderListOfClause :: (Name, [PatternField]) -> Q Clause patternNameBinderListOfClause (conName, sorts) = do   xs <- mapM (\i -> newName ("x" ++ show i)) [1 .. length sorts]   let pats = [ if isBindingField s then VarP x else WildP | (s, x) <- zip sorts xs ]       -- A binder is consed on directly. Only a nested pattern's list is-      -- concatenated, since 'Foil.concatNameBinderLists' is recursive and+      -- concatenated, since 'Control.Monad.Foil.concatNameBinderLists' is recursive and       -- copies its first argument.       list = \case         [] -> ConE 'Foil.NameBinderListEmpty
src/Control/Monad/Foil/Telescope.hs view
@@ -144,8 +144,9 @@ -- unified? -- -- The verdict speaks about binders only, so the renaming it prescribes has to--- be applied before the payloads are compared, which is exactly what--- 'Control.Monad.Free.alphaEquivScoped' does to the body of a scoped term.+-- be applied before the payloads are compared, much as+-- 'Control.Monad.Free.Foil.alphaEquivScoped' takes it into account for the+-- body of a scoped term. -- Comparing them as they stand would report @(A : 𝕌) (x : A)@ and -- @(B : 𝕌) (y : B)@ as different, since the second payloads name different -- binders until the first ones have been identified.
src/Control/Monad/Free/Foil.hs view
@@ -1,1000 +1,61 @@-{-# LANGUAGE DataKinds             #-}-{-# LANGUAGE TypeOperators         #-}-{-# LANGUAGE TypeFamilies         #-}-{-# LANGUAGE DeriveAnyClass        #-}-{-# LANGUAGE DeriveFoldable        #-}-{-# LANGUAGE DeriveFunctor         #-}-{-# LANGUAGE DeriveTraversable     #-}-{-# LANGUAGE DeriveGeneric         #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE LambdaCase            #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds             #-}-{-# LANGUAGE QuantifiedConstraints #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE StandaloneDeriving    #-}-{-# LANGUAGE UndecidableInstances  #-}--- | This module defines a variation of--- free scoped (relative) monads relying on the foil for--- the scope-safe efficient handling of the binders.------ See description of the approach in [«Free Foil: Generating Efficient and Scope-Safe Abstract Syntax»](https://arxiv.org/abs/2405.16384).-module Control.Monad.Free.Foil where--import           Control.DeepSeq-import qualified Control.Monad.Foil.Internal as Foil-import           Data.Bifoldable-import           Data.Bitraversable-import           Data.Bifunctor-import Data.ZipMatchK-import qualified Generics.Kind as Kind-import Generics.Kind (GenericK(..), Field, Exists, Var0, Var1, (:$:), Atom((:@:), Kon), (:+:), (:*:))-import           Data.IntMap.Strict          (IntMap)-import qualified Data.IntMap.Strict          as IntMap-import           Data.Map                    (Map)-import qualified Data.Map                    as Map-import           GHC.Generics                (Generic)-import           Unsafe.Coerce               (unsafeCoerce)---- | Scoped term under a (single) name binder.------ @since 0.0.1-data ScopedAST binder sig n where-  ScopedAST :: binder n l -> AST binder sig l -> ScopedAST binder sig n---- INLINABLE, so that it specialises together with the instance for AST.-instance (forall x y. NFData (binder x y), forall l. NFData (AST binder sig l)) => NFData (ScopedAST binder sig n) where-  {-# INLINABLE rnf #-}-  rnf (ScopedAST binder body) = rnf binder `seq` rnf body---- | A term, generated by a signature 'Bifunctor' @sig@,--- with (free) variables in scope @n@.------ @since 0.0.1-data AST binder sig n where-  -- | A (free) variable in scope @n@.-  Var :: {-# UNPACK #-} !(Foil.Name n) -> AST binder sig n-  -- | A non-variable syntactic construction specified by the signature 'Bifunctor' @sig@.-  Node :: sig (ScopedAST binder sig n) (AST binder sig n) -> AST binder sig n--deriving instance Generic (AST binder sig n)---- | 'rnf' forces the names, the binders, and the nodes through the 'NFData'--- instance of the signature. GHC specialises it where the binder and the--- signature are known. On the λ-terms of the @nfdata@ benchmark, the--- specialised 'rnf' is about four times faster than the general one.-instance (forall x y. NFData (binder x y), forall scope term. (NFData scope, NFData term) => NFData (sig scope term))-  => NFData (AST binder sig n) where-  -- Without a specialisation, each node is forced through dictionaries built-  -- from the quantified constraints. The specialised rnf calls itself on the-  -- subterms and inlines the signature's rnf.-  {-# INLINABLE rnf #-}-  rnf (Var name)  = rnf name-  rnf (Node node) = rnf node--instance GenericK (ScopedAST binder sig) where-  type RepK (ScopedAST binder sig) =-    Exists Foil.S-      (Field (Kon binder :@: Var1 :@: Var0) :*: Field (Kon AST :@: Kon binder :@: Kon sig :@: Var0))-  toK (Kind.Exists (Kind.Field binder Kind.:*: Kind.Field ast)) = ScopedAST binder ast-  fromK (ScopedAST binder ast) = Kind.Exists (Kind.Field binder Kind.:*: Kind.Field ast)--instance GenericK (AST binder sig) where-  type RepK (AST binder sig) =-    Field (Foil.Name :$: Var0)-    :+: Field (sig-                :$: (Kon ScopedAST :@: Kon binder :@: Kon sig :@: Var0)-                :@: (Kon AST :@: Kon binder :@: Kon sig :@: Var0))--instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.Sinkable (ScopedAST binder sig)-instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.Sinkable (AST binder sig)--instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.SinkableK (ScopedAST binder sig)-instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.SinkableK (AST binder sig)--instance Foil.InjectName (AST binder sig) where-  injectName = Var---- * Substitution---- | Substitution for free (scoped monads).------ @since 0.0.1-{-# INLINABLE substitute #-}-substitute-  :: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder, Foil.SinkableK binder)-  => Foil.Scope o-  -> Foil.Substitution (AST binder sig) i o-  -> AST binder sig i-  -> AST binder sig o-substitute scope subst term-  -- An empty substitution maps every name to itself ('addRename' deletes-  -- identity renames), so the result is the very term, and the coercion is-  -- the one 'Foil.sink' performs. Substitutions go empty often: opening a-  -- scoped term with its own binder's name is an identity rename, and under-  -- a deterministic allocation policy a refreshed binder usually keeps its-  -- name, deleting its entry. Binders that shadow the ambient scope are-  -- left as they stand, as on the no-clash path below. A caller that wants-  -- them refreshed asks 'substituteRefreshed'.-  | Foil.nullSubst subst = unsafeCoerce term-  | otherwise = go term-  where-    -- The substitution is known non-empty here, and it can only change-    -- under a binder, so the walk between binders is unchecked and each-    -- binder entry re-enters 'substitute', testing emptiness exactly once.-    go = \case-      Var name -> Foil.lookupSubst subst name-      Node node -> Node (bimap f go node)-    f (ScopedAST binder body) =-      Foil.withRefreshedPattern scope binder $ \extendSubst binder' scope' ->-        let subst' = extendSubst (Foil.sink subst)-            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.------ @since 0.0.3-{-# INLINABLE substituteRefreshed #-}-substituteRefreshed-  :: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder, Foil.SinkableK binder)-  => Foil.Scope o-  -> Foil.Substitution (AST binder sig) i o-  -> AST binder sig i-  -> AST binder 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.withFreshPattern scope binder $ \extendSubst binder' scope' ->-        let subst' = extendSubst (Foil.sink subst)-            body' = substituteRefreshed scope' subst' body-        in ScopedAST binder' body'---- | @'AST' sig@ is a monad relative to 'Foil.Name'.-instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder)-  => Foil.RelMonad Foil.Name (AST binder sig) where-  rreturn = Var-  rbind scope term subst =-    case term of-      Var name  -> subst name-      Node node -> Node (bimap g' g node)-    where-      g x = Foil.rbind scope x subst-      g' (ScopedAST binder body) =-        Foil.withRefreshedPattern' scope binder $ \extendSubst binder' scope' ->-          let subst' = extendSubst subst-           in ScopedAST binder' (Foil.rbind scope' body subst')---- | Substitution for a single generalized pattern.------ @since 0.2.0-{-# INLINABLE substitutePattern #-}-substitutePattern-  :: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder', Foil.CoSinkable binder, Foil.SinkableK binder)-  => Foil.Scope o                           -- ^ Resulting scope.-  -> Foil.Substitution (AST binder sig) n o -- ^ Environment mapping names in scope @n@.-  -> binder' n i                            -- ^ Binders that extend scope @n@ to scope @i@.-  -> [AST binder sig o]                     -- ^ A list of terms intended to serve as-  -> AST binder sig i-  -> AST binder sig o-substitutePattern scope env binders args body =-  substitute scope env' body-  where-    env' = Foil.addSubstPattern env binders args---- * Transformations between signatures---- | Fold a term bottom-up into a scope-indexed carrier @r@. A variable goes--- through the first function, a scoped subterm through the second, given its--- binder and the fold of its body, and a node through the third, once its--- subterms are folded. The scope indices stay as they are, so the carrier--- can be another kind of term, as in 'hoistAST', or anything indexed by the--- scope, such as @'Const' Int@ for a node count.------ To change the binder type as well, map it in the second function:------ > foldAST Var (\binder body -> ScopedAST (toBinder binder) body) Node------ @since 0.6.0-{-# INLINABLE foldAST #-}-foldAST-  :: forall binder sig r s n. Bifunctor sig-  => (forall m. Foil.Name m -> r m)             -- ^ A variable.-  -> (forall m l. binder m l -> r l -> s m)     -- ^ A scoped subterm, from its binder and the fold of its body.-  -> (forall m. sig (s m) (r m) -> r m)         -- ^ A node, its subterms folded.-  -> AST binder sig n-  -> r n-foldAST var scoped node = go-  where-    go :: forall m. AST binder sig m -> r m-    go (Var x)  = var x-    go (Node t) = node (bimap (\(ScopedAST binder body) -> scoped binder (go body)) go t)---- | Change the signature of a term by a natural transformation of--- signatures, keeping the binders, as @hoistFree@ of the free package does--- for a free monad. Each node is rebuilt, so this costs a traversal.------ @since 0.6.0-{-# INLINABLE hoistAST #-}-hoistAST-  :: Bifunctor sig-  => (forall a b. sig a b -> sig' a b)-  -> AST binder sig n-  -> AST binder sig' n-hoistAST f = foldAST Var ScopedAST (Node . f)---- * \(\alpha\)-equivalence---- | Refresh (force) all binders in a term, minimizing the used indices.------ @since 0.0.3-{-# INLINABLE refreshAST #-}-refreshAST-  :: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder, Foil.SinkableK binder)-  => Foil.Scope n-  -> AST binder sig n-  -> AST binder sig n-refreshAST scope = \case-  t@Var{} -> t-  Node t -> Node (bimap (refreshScopedAST scope) (refreshAST scope) t)---- | Similar to `refreshAST`, but for scoped terms.------ @since 0.0.3-{-# INLINABLE refreshScopedAST #-}-refreshScopedAST :: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder, Foil.SinkableK binder)-  => Foil.Scope n-  -> ScopedAST binder sig n-  -> ScopedAST binder sig n-refreshScopedAST scope (ScopedAST binder body) =-  Foil.withFreshPattern scope binder $ \extendSubst binder' scope' ->-    let subst = extendSubst (Foil.sink Foil.identitySubst)-    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 renames every binder on both sides--- unconditionally, so it does strictly more work. It remains as an--- independent implementation of the same test.------ @since 0.0.3-{-# INLINABLE alphaEquivRefreshed #-}-alphaEquivRefreshed-  :: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)-  => Foil.Scope n-  -> AST binder sig n-  -> AST binder sig n-  -> Bool-alphaEquivRefreshed scope t1 t2 = refreshAST scope t1 `unsafeEqAST` refreshAST scope t2---- | A term is a scope-indexed value that can be compared up to α, which is what--- a pattern carrying terms as payloads needs of them.-instance (Bitraversable sig, ZipMatchK sig, Foil.UnifiablePattern binder, Foil.SinkableK binder)-    => Foil.AlphaEquiv (AST binder sig) where-  alphaEquivIn = alphaEquiv---- | \(\alpha\)-equivalence check for two terms in one scope--- via unification of bound variables (via 'unifyNameBinders').------ When two matching binders coincide, comparison continues with no work at--- all. When they differ, the prescribed renaming is /threaded down the--- recursion/ (see 'alphaEquivEnv') and consulted at variables only, so--- nothing is ever copied. Applying the renaming eagerly instead would--- materialise a renamed copy of the remaining body at every mismatched--- binder, which is quadratic on a chain of them.------ @since 0.0.3-{-# INLINABLE alphaEquiv #-}-alphaEquiv-  :: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)-  => Foil.Scope n-  -> AST binder sig n-  -> AST binder sig n-  -> Bool-alphaEquiv _scope (Var x) (Var y) = x == y-alphaEquiv scope (Node l) (Node r) =-  case zipMatchWith2 (unit . alphaEquivScoped scope) (unit . alphaEquiv scope) l r of-    Nothing -> False-    Just _  -> True-  where-    unit f x = if f x then Just () else Nothing-alphaEquiv _ _ _ = False---- | Same as 'alphaEquiv' but for scoped terms.------ While the binders of the two sides coincide, this runs with no renaming--- state at all. The first pair that differs switches to 'alphaEquivEnv',--- which threads the renamings down and switches back when they empty out.------ @since 0.0.3-{-# INLINABLE alphaEquivScoped #-}-alphaEquivScoped-  :: forall sig binder n. (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)-  => Foil.Scope n-  -> ScopedAST binder sig n-  -> ScopedAST binder sig n-  -> Bool-alphaEquivScoped scope-  (ScopedAST binder1 body1)-  (ScopedAST binder2 body2) =-    case Foil.unifyPatternsIn scope binder1 binder2 of-      -- the binders coincide: compare the bodies as they stand-      Foil.SameNameBinders{} ->  -- after seeing this we know that body scopes are the same-        case Foil.assertDistinct binder1 of-          Foil.Distinct ->-            let scope1 = Foil.extendScopePattern binder1 scope-            in alphaEquiv scope1 body1 body2-      -- the left binder is renamed towards the right one-      Foil.RenameLeftNameBinder _ rename1to2 ->-        case Foil.assertDistinct binder2 of-          Foil.Distinct ->-            let scope2 = Foil.extendScopePattern binder2 scope-                pairs = [ (Foil.nameId x, renamedId rename1to2 x)-                        | x <- Foil.namesOfPattern binder1 ]-            in enterEnv pairs scope2 body1 body2-      -- the right binder is renamed towards the left one-      Foil.RenameRightNameBinder _ rename2to1 ->-        case Foil.assertDistinct binder1 of-          Foil.Distinct ->-            let scope1 = Foil.extendScopePattern binder1 scope-                pairs = [ (renamedId rename2to1 y, Foil.nameId y)-                        | y <- Foil.namesOfPattern binder2 ]-            in enterEnv pairs scope1 body1 body2-      -- both are renamed towards a unified pattern: pair the two sides'-      -- names through the unified name each maps to-      Foil.RenameBothBinders binder' rename1 rename2 ->-        case Foil.assertDistinct binder' of-          Foil.Distinct ->-            let scope' = Foil.extendScopePattern binder' scope-                leftU = IntMap.fromList-                  [ (renamedId rename1 x, Foil.nameId x)-                  | x <- Foil.namesOfPattern binder1 ]-                rightU = IntMap.fromList-                  [ (renamedId rename2 y, Foil.nameId y)-                  | y <- Foil.namesOfPattern binder2 ]-                pairs = IntMap.elems (IntMap.intersectionWith (,) leftU rightU)-            in enterEnv pairs scope' body1 body2-      -- if we cannot unify patterns then scopes are not alpha-equivalent-      Foil.NotUnifiable -> False-  where-    enterEnv-      :: forall m l1 l2. Foil.Distinct m-      => [(Int, Int)] -> Foil.Scope m-      -> AST binder sig l1 -> AST binder sig l2 -> Bool-    enterEnv pairs scope' = bindPairs 0 IntMap.empty IntMap.empty pairs scope'---- | The raw name a verdict's renaming assigns to a pattern's name.------ @since 0.4.0-renamedId :: (Foil.NameBinder n a -> Foil.NameBinder n b) -> Foil.Name a -> Int-renamedId rename = Foil.nameId . Foil.nameOf . rename . Foil.UnsafeNameBinder---- | Bind the paired names of a binder pair. A pair whose names coincide--- shadows both sides identically and is deleted from the environments. A pair--- whose names differ binds both to one fresh level. Continues with--- 'alphaEquivEnv' on the bodies.------ @since 0.4.0-bindPairs-  :: forall sig binder m l1 l2. (Bitraversable sig, ZipMatchK sig, Foil.Distinct m, Foil.UnifiablePattern binder, Foil.SinkableK binder)-  => Int -> IntMap Int -> IntMap Int -> [(Int, Int)]-  -> Foil.Scope m-  -> AST binder sig l1 -> AST binder sig l2 -> Bool-bindPairs lvl envL envR pairs scope body1 body2 = case pairs of-  [] -> alphaEquivEnv lvl envL envR scope body1 body2-  ((x, y) : rest)-    | x == y    -> bindPairs lvl (IntMap.delete x envL) (IntMap.delete y envR) rest scope body1 body2-    | otherwise -> bindPairs (lvl + 1) (IntMap.insert x lvl envL) (IntMap.insert y lvl envR) rest scope body1 body2---- | The renaming-threading worker behind 'alphaEquiv': compare two terms--- under partial renamings of their names into shared /levels/.------ Each environment maps a raw name to the level of the binder pair that bound--- it on the comparison path, and a name outside its environment stands for--- itself. A variable occurrence then compares as a level against a level, or--- as a raw name against a raw name, and the two can never be conflated. This--- is what makes threading sound where applying a raw renaming at the variables--- would not be, since a renamed name could collide with one that passes--- through unchanged and happens to share the target spelling. Levels are also--- why no capture check is needed: a level is never a name, so there is nothing--- for a binder to capture.------ A binder pair whose names coincide /deletes/ those names from both--- environments, the pair shadowing both sides identically. When the--- environments empty out the comparison drops back to the stateless--- 'alphaEquiv', so only the region of the terms below a mismatched binder,--- and above the point where the mismatch is shadowed away, pays for the--- threading at all.------ The indices of the two terms are deliberately independent, in the style--- of 'unsafeEqAST': the terms are never renamed into a common scope, so--- no common index exists to give them.------ @since 0.4.0-{-# INLINABLE alphaEquivEnv #-}-alphaEquivEnv-  :: forall sig binder n n1 n2. (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)-  => Int          -- ^ Next fresh level.-  -> IntMap Int   -- ^ Left renaming: raw name to the level that bound it.-  -> IntMap Int   -- ^ Right renaming.-  -> Foil.Scope n -- ^ Scope along the unified path (consulted by 'Foil.unifyPatternsIn').-  -> AST binder sig n1-  -> AST binder sig n2-  -> Bool-alphaEquivEnv lvl envL envR scope t1 t2-  | IntMap.null envL && IntMap.null envR =-      -- The renamings have emptied out (or never held anything): the-      -- terms coincide raw-for-raw from here on, so compare them where-      -- they stand. The coercion brings both indices to the scope's,-      -- which is the unified path the comparison speaks of.-      alphaEquiv scope (unsafeCoerce t1 :: AST binder sig n) (unsafeCoerce t2 :: AST binder sig n)-  | otherwise = case (t1, t2) of-      (Var x, Var y) ->-        case (IntMap.lookup (Foil.nameId x) envL, IntMap.lookup (Foil.nameId y) envR) of-          (Just k1, Just k2) -> k1 == k2-          (Nothing, Nothing) -> Foil.nameId x == Foil.nameId y-          _                  -> False-      (Node l, Node r) ->-        case zipMatchWith2-               (unit . alphaEquivScopedEnv lvl envL envR scope)-               (unit . alphaEquivEnv lvl envL envR scope) l r of-          Nothing -> False-          Just _  -> True-      _ -> False-  where-    unit f x = if f x then Just () else Nothing---- | The scoped half of 'alphaEquivEnv': get the verdict from--- 'Foil.unifyPatternsIn', extend the environments as it prescribes, and--- recurse into the bodies as they stand.------ @since 0.4.0-{-# INLINABLE alphaEquivScopedEnv #-}-alphaEquivScopedEnv-  :: forall sig binder n n1 n2. (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)-  => Int-  -> IntMap Int-  -> IntMap Int-  -> Foil.Scope n-  -> ScopedAST binder sig n1-  -> ScopedAST binder sig n2-  -> Bool-alphaEquivScopedEnv lvl envL envR scope scoped1 scoped2 =-  -- The scoped terms are compared where they stand; the coercion only-  -- brings their indices to the scope's, which is the unified path the-  -- environments and the verdicts speak of.-  case (unsafeCoerce scoped1 :: ScopedAST binder sig n, unsafeCoerce scoped2 :: ScopedAST binder sig n) of-    (ScopedAST binder1 body1, ScopedAST binder2 body2) ->-      case Foil.unifyPatternsIn scope binder1 binder2 of-        -- the binders coincide: the pair shadows both sides identically-        Foil.SameNameBinders{} ->-          case Foil.assertDistinct binder1 of-            Foil.Distinct ->-              let scope' = Foil.extendScopePattern binder1 scope-                  names = map Foil.nameId (Foil.namesOfPattern binder1)-                  envL' = deleteAll names envL-                  envR' = deleteAll names envR-               in alphaEquivEnv lvl envL' envR' scope' body1 body2-        -- the left binder is renamed towards the right one-        Foil.RenameLeftNameBinder _ rename1to2 ->-          case Foil.assertDistinct binder2 of-            Foil.Distinct ->-              let scope' = Foil.extendScopePattern binder2 scope-                  pairs = [ (Foil.nameId x, renamedId rename1to2 x)-                          | x <- Foil.namesOfPattern binder1 ]-               in bindPairs lvl envL envR pairs scope' body1 body2-        -- the right binder is renamed towards the left one-        Foil.RenameRightNameBinder _ rename2to1 ->-          case Foil.assertDistinct binder1 of-            Foil.Distinct ->-              let scope' = Foil.extendScopePattern binder1 scope-                  pairs = [ (renamedId rename2to1 y, Foil.nameId y)-                          | y <- Foil.namesOfPattern binder2 ]-               in bindPairs lvl envL envR pairs scope' body1 body2-        -- both are renamed towards a unified pattern: pair the two sides'-        -- names through the unified name each maps to-        Foil.RenameBothBinders binder' rename1 rename2 ->-          case Foil.assertDistinct binder' of-            Foil.Distinct ->-              let scope' = Foil.extendScopePattern binder' scope-                  leftU = IntMap.fromList-                    [ (renamedId rename1 x, Foil.nameId x)-                    | x <- Foil.namesOfPattern binder1 ]-                  rightU = IntMap.fromList-                    [ (renamedId rename2 y, Foil.nameId y)-                    | y <- Foil.namesOfPattern binder2 ]-                  pairs = IntMap.elems (IntMap.intersectionWith (,) leftU rightU)-               in bindPairs lvl envL envR pairs scope' body1 body2-        Foil.NotUnifiable -> False-  where-    deleteAll names env = case names of-      []       -> env-      (i : is) -> deleteAll is (IntMap.delete i env)---- ** 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).------ @since 0.0.3-{-# INLINABLE unsafeEqAST #-}-unsafeEqAST-  :: (Bitraversable sig, ZipMatchK sig, Foil.UnifiablePattern binder, Foil.Distinct n, Foil.Distinct l)-  => AST binder sig n-  -> AST binder sig l-  -> Bool-unsafeEqAST (Var x) (Var y) = Foil.nameId x == Foil.nameId y-unsafeEqAST (Node t1) (Node t2) =-  case zipMatchWith2 (unit . unsafeEqScopedAST) (unit . unsafeEqAST) t1 t2 of-    Nothing -> False-    Just _  -> True-  where-    unit f x = if f x then Just () else Nothing-unsafeEqAST _ _ = False---- | A version of 'unsafeEqAST' for scoped terms.------ @since 0.0.3-{-# INLINABLE unsafeEqScopedAST #-}-unsafeEqScopedAST-  :: (Bitraversable sig, ZipMatchK sig, Foil.UnifiablePattern binder, Foil.Distinct n, Foil.Distinct l)-  => ScopedAST binder sig n-  -> ScopedAST binder sig l-  -> Bool-unsafeEqScopedAST (ScopedAST binder1 body1) (ScopedAST binder2 body2) = and-  [ Foil.unsafeEqPattern binder1 binder2-  , case (Foil.assertDistinct binder1, Foil.assertDistinct binder2) of-      (Foil.Distinct, Foil.Distinct) -> body1 `unsafeEqAST` body2-  ]---- * Converting to and from free foil---- ** Convert to free foil---- | An identifier a raw term mentions that the names given for conversion do--- not account for.------ Note what this does and does not carry. It cannot carry a position: the--- conversion functions are generic in the raw term and only ever see it through--- @toSig@, so a source location, if the syntax has one, is not theirs to read.--- What they do know, and a caller checking names beforehand does not, is which--- names were in scope /at the occurrence/, the binders passed on the way down--- included. That is what a \"did you mean\" needs.------ @since 0.4.0-data UnresolvedName rawIdent = UnresolvedName-  { unresolvedIdent   :: rawIdent-    -- ^ The identifier that did not resolve.-  , unresolvedInScope :: [rawIdent]-    -- ^ What was in scope where it occurred.-  } deriving (Eq, Show, Functor, Foldable, Traversable)---- | The identifiers a raw term mentions that a set of names cannot resolve, in--- the order they occur.------ This is 'unsafeConvertToAST' with the conversion left out, so it descends--- under binders in the same way and accounts for what they bind.------ @since 0.4.0-unresolvedNames-  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.-     (Foil.Distinct n, Bifoldable sig, Ord rawIdent, Foil.CoSinkable binder)-  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))-  -- ^ Unpeel one syntax node (or a variable) from a raw term.-  -> (forall x z. Foil.Distinct x-      => Foil.Scope x-      -> Map rawIdent (Foil.Name x)-      -> rawPattern-      -> (forall y. Foil.DExt x y-          => binder x y-          -> Map rawIdent (Foil.Name y)-          -> z)-      -> z)-  -- ^ Convert raw pattern into a scope-safe pattern.-  -> (rawScopedTerm -> rawTerm)-  -- ^ Extract a term from a scoped term (or crash).-  -> Foil.Scope n-  -- ^ Resulting scope of the constructed term.-  -> Map rawIdent (Foil.Name n)-  -- ^ Known names of free variables in scope @n@.-  -> rawTerm-  -- ^ Raw term.-  -> [UnresolvedName rawIdent]-unresolvedNames toSig fromRawPattern getScopedTerm = go-  where-    go :: forall x. Foil.Distinct x-       => Foil.Scope x -> Map rawIdent (Foil.Name x) -> rawTerm -> [UnresolvedName rawIdent]-    go scope names t = case toSig t of-      Left x-        | Map.member x names -> []-        | otherwise          -> [UnresolvedName x (Map.keys names)]-      Right node -> bifoldMap (goScoped scope names) (go scope names) node--    goScoped :: forall x. Foil.Distinct x-             => Foil.Scope x -> Map rawIdent (Foil.Name x)-             -> (rawPattern, rawScopedTerm) -> [UnresolvedName rawIdent]-    goScoped scope names (pat, scopedTerm) =-      fromRawPattern scope names pat $ \binder' names' ->-        go (Foil.extendScopePattern binder' scope) names' (getScopedTerm scopedTerm)---- | Convert a raw term into a scope-safe term, reporting the first identifier--- that does not resolve.------ One pass, short-circuiting at the first failure, so a term that resolves--- costs no more than 'unsafeConvertToAST' does. The report is complete for--- that one identifier, since 'unresolvedInScope' is built where the conversion--- fails and is never computed on the way through.------ A caller wanting /every/ unresolved identifier rather than the first pays a--- second pass for it, with 'unresolvedNames'. The successful path stays fast--- that way, and a failure can afford to be walked again for a better message.------ @since 0.4.0-tryConvertToAST-  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.-     (Foil.Distinct n, Bitraversable sig, Ord rawIdent,-      Foil.CoSinkable binder, Foil.SinkableK binder)-  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))-  -- ^ Unpeel one syntax node (or a variable) from a raw term.-  -> (forall x z. Foil.Distinct x-      => Foil.Scope x-      -> Map rawIdent (Foil.Name x)-      -> rawPattern-      -> (forall y. Foil.DExt x y-          => binder x y-          -> Map rawIdent (Foil.Name y)-          -> z)-      -> z)-  -- ^ Convert raw pattern into a scope-safe pattern.-  -> (rawScopedTerm -> rawTerm)-  -- ^ Extract a term from a scoped term (or crash).-  -> Foil.Scope n-  -- ^ Resulting scope of the constructed term.-  -> Map rawIdent (Foil.Name n)-  -- ^ Known names of free variables in scope @n@.-  -> rawTerm-  -- ^ Raw term.-  -> Either (UnresolvedName rawIdent) (AST binder sig n)-tryConvertToAST toSig fromRawPattern getScopedTerm scope names =-  tryConvertToASTWith toSig fromRawPattern getScopedTerm scope names Map.empty---- | Convert a raw term into a scope-safe term, resolving some identifiers to--- whole terms rather than to variables.------ The extra table is what a language with /constants/ needs: an identifier that--- denotes a top-level declaration, a primitive, or an abbreviation stands for--- something that is not a variable, and resolving it during conversion is the--- only place where the binders are known. Doing it in a pass of one's own means--- writing a binder-aware traversal of the raw syntax by hand.------ Variables win: the table of names is consulted first, so a binder shadows an--- entry here, and an entry here shadows nothing. The table is sunk when going--- under a binder, exactly as the names are, so its entries may mention anything--- in scope where conversion started and need not be closed.------ @since 0.4.0-tryConvertToASTWith-  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.-     (Foil.Distinct n, Bitraversable sig, Ord rawIdent,-      Foil.CoSinkable binder, Foil.SinkableK binder)-  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))-  -- ^ Unpeel one syntax node (or a variable) from a raw term.-  -> (forall x z. Foil.Distinct x-      => Foil.Scope x-      -> Map rawIdent (Foil.Name x)-      -> rawPattern-      -> (forall y. Foil.DExt x y-          => binder x y-          -> Map rawIdent (Foil.Name y)-          -> z)-      -> z)-  -- ^ Convert raw pattern into a scope-safe pattern.-  -> (rawScopedTerm -> rawTerm)-  -- ^ Extract a term from a scoped term (or crash).-  -> Foil.Scope n-  -- ^ Resulting scope of the constructed term.-  -> Map rawIdent (Foil.Name n)-  -- ^ Known names of free variables in scope @n@.-  -> Map rawIdent (AST binder sig n)-  -- ^ Identifiers that denote a term rather than a variable.-  -> rawTerm-  -- ^ Raw term.-  -> Either (UnresolvedName rawIdent) (AST binder sig n)-tryConvertToASTWith toSig fromRawPattern getScopedTerm = go-  where-    go :: forall x. Foil.Distinct x-       => Foil.Scope x -> Map rawIdent (Foil.Name x)-       -> Map rawIdent (AST binder sig x) -> rawTerm-       -> Either (UnresolvedName rawIdent) (AST binder sig x)-    go scope names terms t = case toSig t of-      Left x -> case Map.lookup x names of-        Just name -> Right (Var name)-        Nothing   -> case Map.lookup x terms of-          Just term -> Right term-          Nothing   -> Left (UnresolvedName x (Map.keys names <> Map.keys terms))-      Right node ->-        Node <$> bitraverse (goScoped scope names terms) (go scope names terms) node--    goScoped :: forall x. Foil.Distinct x-             => Foil.Scope x -> Map rawIdent (Foil.Name x)-             -> Map rawIdent (AST binder sig x)-             -> (rawPattern, rawScopedTerm)-             -> Either (UnresolvedName rawIdent) (ScopedAST binder sig x)-    goScoped scope names terms (pat, scopedTerm) =-      fromRawPattern scope names pat $ \binder' names' ->-        ScopedAST binder'-          <$> go (Foil.extendScopePattern binder' scope) names'-                 (Foil.sink1 terms) (getScopedTerm scopedTerm)---- | Convert a raw term into a scope-safe term, calling 'error' on an--- identifier that does not resolve.------ Prefer 'tryConvertToAST', which reports such identifiers. This is for callers--- that have already established that every name resolves.------ @since 0.4.0-unsafeConvertToAST-  :: (Foil.Distinct n, Bifunctor sig, Ord rawIdent, Foil.CoSinkable binder)-  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))-  -- ^ Unpeel one syntax node (or a variable) from a raw term.-  -> (forall x z. Foil.Distinct x-      => Foil.Scope x-      -> Map rawIdent (Foil.Name x)-      -> rawPattern-      -> (forall y. Foil.DExt x y-          => binder x y-          -> Map rawIdent (Foil.Name y)-          -> z)-      -> z)-  -- ^ Convert raw pattern into a scope-safe pattern.-  -> (rawScopedTerm -> rawTerm)-  -- ^ Extract a term from a scoped term (or crash).-  -> Foil.Scope n-  -- ^ Resulting scope of the constructed term.-  -> Map rawIdent (Foil.Name n)-  -- ^ Known names of free variables in scope @n@.-  -> rawTerm-  -- ^ Raw term.-  -> AST binder sig n-unsafeConvertToAST toSig fromRawPattern 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-        (unsafeConvertToScopedAST toSig fromRawPattern getScopedTerm scope names)-        (unsafeConvertToAST toSig fromRawPattern getScopedTerm scope names)-        node---- | Same as 'unsafeConvertToAST' but for scoped terms.------ @since 0.4.0-unsafeConvertToScopedAST-  :: (Foil.Distinct n, Bifunctor sig, Ord rawIdent, Foil.CoSinkable binder)-  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))-  -- ^ Unpeel one syntax node (or a variable) from a raw term.-  -> (forall x z. Foil.Distinct x-      => Foil.Scope x-      -> Map rawIdent (Foil.Name x)-      -> rawPattern-      -> (forall y. Foil.DExt x y-          => binder x y-          -> Map rawIdent (Foil.Name y)-          -> z)-      -> z)-  -- ^ Convert raw pattern into a scope-safe pattern.-  -> (rawScopedTerm -> rawTerm)-  -- ^ Extract a term from a scoped term (or crash).-  -> Foil.Scope n-  -- ^ Resulting scope of the constructed term.-  -> Map rawIdent (Foil.Name n)-  -- ^ Known names of free variables in scope @n@.-  -> (rawPattern, rawScopedTerm)-  -- ^ A pair of a pattern and a corresponding scoped term.-  -> ScopedAST binder sig n-unsafeConvertToScopedAST toSig fromRawPattern getScopedTerm scope names (pat, scopedTerm) =-  fromRawPattern scope names pat $ \binder' names' ->-    let scope' = Foil.extendScopePattern binder' scope-     in ScopedAST binder' (unsafeConvertToAST toSig fromRawPattern getScopedTerm scope' names' (getScopedTerm scopedTerm))---- ** Convert from free foil---- | Convert a scope-safe term back into a raw term.------ @since 0.0.3-convertFromAST-  :: Bifunctor sig-  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)-  -- ^ Peel back one layer of syntax.-  -> (rawIdent -> rawTerm)-  -- ^ Convert identifier into a raw variable term.-  -> (forall x y. binder x y -> rawPattern)-  -- ^ Convert scope-safe pattern into a raw pattern.-  -> (rawTerm -> rawScopedTerm)-  -- ^ Wrap raw term into a scoped term.-  -> (Int -> rawIdent)-  -- ^ Convert underlying integer identifier of a bound variable into a raw identifier.-  -> AST binder sig n-  -- ^ Scope-safe term.-  -> 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---- | Same as 'convertFromAST' but for scoped terms.------ @since 0.0.3-convertFromScopedAST-  :: Bifunctor sig-  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)-  -- ^ Peel back one layer of syntax.-  -> (rawIdent -> rawTerm)-  -- ^ Convert identifier into a raw variable term.-  -> (forall x y. binder x y -> rawPattern)-  -- ^ Convert scope-safe pattern into a raw pattern.-  -> (rawTerm -> rawScopedTerm)-  -- ^ Wrap raw term into a scoped term.-  -> (Int -> rawIdent)-  -- ^ Convert underlying integer identifier of a bound variable into a raw identifier.-  -> ScopedAST binder sig n-  -- ^ Scope-safe scoped term.-  -> (rawPattern, rawScopedTerm)-convertFromScopedAST fromSig fromVar makePattern makeScoped f = \case-  ScopedAST binder body ->-    ( makePattern binder-    , makeScoped (convertFromAST fromSig fromVar makePattern makeScoped f body))---- | Convert a scope-safe term back into a raw term, naming the variables that--- occur /free in the whole term/ separately from the bound ones.------ 'convertFromAST' applies one naming function to every variable it meets,--- bound or free, and gives it only a raw name. That is often not enough, since--- raw names are not unique across scope indices: a binder inside a term may--- share one with a name of the ambient scope, so naming by raw name alone can--- print a bound variable as whatever the ambient scope calls that name.------ Keeping the typed name is what distinguishes them, and 'Foil.unsinkNamePattern'--- is the operation for it: composing one per binder on the way down builds a--- @'Foil.Name' x -> 'Maybe' ('Foil.Name' n)@ that answers exactly the question.------ @since 0.4.0-convertFromASTWith-  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.-     (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder)-  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)-  -- ^ Peel back one layer of syntax.-  -> (rawIdent -> rawTerm)-  -- ^ Convert identifier into a raw variable term.-  -> (forall x y. binder x y -> rawPattern)-  -- ^ Convert scope-safe pattern into a raw pattern.-  -> (rawTerm -> rawScopedTerm)-  -- ^ Wrap raw term into a scoped term.-  -> (Foil.Name n -> rawIdent)-  -- ^ Name a variable that is free in the whole term.-  -> (Int -> rawIdent)-  -- ^ Name a bound variable, from its underlying integer identifier.-  -> AST binder sig n-  -- ^ Scope-safe term.-  -> rawTerm-convertFromASTWith fromSig fromVar makePattern makeScoped freeName boundName =-    go Just-  where-    go :: forall x. Foil.Distinct x-       => (Foil.Name x -> Maybe (Foil.Name n)) -> AST binder sig x -> rawTerm-    go unsink = \case-      Var x -> fromVar $ case unsink x of-        Just name -> freeName name-        Nothing   -> boundName (Foil.nameId x)-      Node node -> fromSig (bimap (goScoped unsink) (go unsink) node)--    goScoped :: forall x. Foil.Distinct x-             => (Foil.Name x -> Maybe (Foil.Name n))-             -> ScopedAST binder sig x -> (rawPattern, rawScopedTerm)-    goScoped unsink (ScopedAST binder body) =-      case Foil.assertDistinct binder of-        Foil.Distinct ->-          ( makePattern binder-          , makeScoped-              (go (\name -> Foil.unsinkNamePattern binder name >>= unsink) body) )---- ** Unsinking AST---- | The support of a term: exactly the names that occur free in it.------ This is the annotation that co-de-Bruijn syntax carries intrinsically and--- that the foil, having global names and therefore free weakening, does not.--- Computing it is \(O(size)\); a client that restricts often should cache it.------ @since 0.4.0-supportOf-  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)-  => AST binder sig n -> Foil.NameSet n-supportOf = \case-  Var name  -> Foil.nameSetSingleton name-  Node node -> bifoldMap supportOfScopedAST supportOf node---- | The support of a scoped term, in the scope /outside/ its binder.------ @since 0.4.0-supportOfScopedAST-  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)-  => ScopedAST binder sig n -> Foil.NameSet n-supportOfScopedAST (ScopedAST binder body) =-  case Foil.assertDistinct binder of-    Foil.Distinct -> Foil.unsinkNameSet binder (supportOf body)---- | Cut a term down to the scope of exactly the names it uses.------ This is the a-priori form of restriction, and the cheap one: the term--- inhabits the smaller scope /by construction/, so nothing is tested and--- nothing can fail. @'Foil.Ext' m n@ comes back with it, so the term can be--- 'Foil.sink'ed to where it came from for free.------ Verifying a declared dependency, such as a @uses@ clause or a module's--- parameters, is this plus a comparison: compute the scope a term really--- inhabits, and check the declared one against it.------ @since 0.4.0-withRelevantScope-  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)-  => AST binder sig n-  -> (forall m. (Foil.Ext m n, Foil.Distinct m)-      => Foil.Scope m -> AST binder sig m -> r)-  -> r-withRelevantScope term cont =-  Foil.withRestrictedScope (supportOf term) $ \scope ->-    cont scope (unsafeCoerce term)---- | Unsink an AST from a larger scope to a smaller scope.------ This is the a-posteriori form, and the one that has to be paid for: the--- term's support is computed and compared against the scope. When it succeeds--- the term itself is untouched, since restriction of a term that does inhabit--- the smaller scope is a coercion.------ @since 0.3.0-unsinkAST-  :: (Foil.Distinct l, Foil.CoSinkable binder, Bifoldable sig)-  => Foil.Scope n -> AST binder sig l -> Maybe (AST binder sig n)-unsinkAST scope term-  | Foil.nameSetSubsetOfScope (supportOf term) scope = Just (unsafeCoerce term)-  | otherwise = Nothing---- | Get the free variables of an AST.------ These come from 'supportOf', so they are distinct and in ascending order of--- their identifiers.------ @since 0.3.0-freeVarsOf-  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)-  => AST binder sig n -> [Foil.Name n]-freeVarsOf = Foil.nameSetToList . supportOf---- | Get the free variables of a scoped AST, in the scope outside its binder.------ @since 0.3.0-freeVarsOfScopedAST-  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)-  => ScopedAST binder sig n -> [Foil.Name n]-freeVarsOfScopedAST = Foil.nameSetToList . supportOfScopedAST+-- | This module defines a variation of+-- free scoped (relative) monads relying on the foil for+-- the scope-safe efficient handling of the binders.+--+-- See description of the approach in [«Free Foil: Generating Efficient and Scope-Safe Abstract Syntax»](https://arxiv.org/abs/2405.16384).+module Control.Monad.Free.Foil (+  -- * Terms+  AST(..),+  ScopedAST(..),++  -- * Substitution+  substitute,+  substituteRefreshed,+  substitutePattern,++  -- * Transformations between signatures+  foldAST,+  hoistAST,++  -- * \(\alpha\)-equivalence+  refreshAST,+  refreshScopedAST,+  alphaEquivRefreshed,+  alphaEquiv,+  alphaEquivScoped,+  renamedId,+  bindPairs,+  alphaEquivEnv,+  alphaEquivScopedEnv,++  -- ** Unsafe equality checks+  unsafeEqAST,+  unsafeEqScopedAST,+  unsafeEqASTIn,+  unsafeEqScopedASTIn,++  -- * Converting to and from free foil+  -- ** Convert to free foil+  UnresolvedName(..),+  unresolvedNames,+  unresolvedNamesWith,+  tryConvertToAST,+  tryConvertToASTWith,+  unsafeConvertToAST,+  unsafeConvertToScopedAST,++  -- ** Convert from free foil+  convertFromAST,+  convertFromScopedAST,+  convertFromASTWith,++  -- ** Unsinking AST+  supportOf,+  supportOfScopedAST,+  withRelevantScope,+  unsinkAST,+  freeVarsOf,+  freeVarsOfScopedAST,+) where++import           Control.Monad.Free.Foil.Internal
src/Control/Monad/Free/Foil/Binary.hs view
@@ -16,7 +16,7 @@ -- and nothing else in the library does. (The dependency this costs is -- @binary@, a GHC boot library.) ----- Note that decoding /mints/ scope evidence: a 'Foil.Name' comes back at+-- Note that decoding /mints/ scope evidence: a t'Control.Monad.Foil.Name' comes back at -- whatever scope index the context asks for, and the existential scope -- under a binder is chosen arbitrarily. Thus the instances are a trust -- boundary, in the sense of 'Control.Monad.Foil.Blocks.checkExtScope'. The
+ src/Control/Monad/Free/Foil/Internal.hs view
@@ -0,0 +1,1222 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE DeriveAnyClass        #-}+{-# LANGUAGE DeriveFoldable        #-}+{-# LANGUAGE DeriveFunctor         #-}+{-# LANGUAGE DeriveTraversable     #-}+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds             #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE StandaloneDeriving    #-}+{-# LANGUAGE UndecidableInstances  #-}+-- | The implementation of "Control.Monad.Free.Foil", together with the+-- definitions it does not export, such as the loops behind α-equivalence.+-- Use "Control.Monad.Free.Foil" instead, unless you need those.+--+-- @since 0.6.1+module Control.Monad.Free.Foil.Internal where++import           Control.DeepSeq+import qualified Control.Monad.Foil.Internal as Foil+import           Data.Bifoldable+import           Data.Bitraversable+import           Data.Bifunctor+import Data.ZipMatchK+import qualified Generics.Kind as Kind+import Generics.Kind (GenericK(..), Field, Exists, Var0, Var1, (:$:), Atom((:@:), Kon), (:+:), (:*:))+import           Data.IntMap.Strict          (IntMap)+import qualified Data.IntMap.Strict          as IntMap+import           Data.Map                    (Map)+import qualified Data.Map                    as Map+import           GHC.Generics                (Generic)+import           Unsafe.Coerce               (unsafeCoerce)++-- | Scoped term under a binder, which may be a pattern binding several names.+--+-- @since 0.0.1+data ScopedAST binder sig n where+  ScopedAST :: binder n l -> AST binder sig l -> ScopedAST binder sig n++-- INLINABLE, so that it specialises together with the instance for AST.+instance (forall x y. NFData (binder x y), forall l. NFData (AST binder sig l)) => NFData (ScopedAST binder sig n) where+  {-# INLINABLE rnf #-}+  rnf (ScopedAST binder body) = rnf binder `seq` rnf body++-- | A term, generated by a signature 'Bifunctor' @sig@,+-- with (free) variables in scope @n@.+--+-- @since 0.0.1+data AST binder sig n where+  -- | A (free) variable in scope @n@.+  Var :: {-# UNPACK #-} !(Foil.Name n) -> AST binder sig n+  -- | A non-variable syntactic construction specified by the signature 'Bifunctor' @sig@.+  Node :: sig (ScopedAST binder sig n) (AST binder sig n) -> AST binder sig n++deriving instance Generic (AST binder sig n)++-- | 'rnf' forces the names, the binders, and the nodes through the 'NFData'+-- instance of the signature. GHC specialises it where the binder and the+-- signature are known. On the λ-terms of the @nfdata@ benchmark, the+-- specialised 'rnf' is about four times faster than the general one.+instance (forall x y. NFData (binder x y), forall scope term. (NFData scope, NFData term) => NFData (sig scope term))+  => NFData (AST binder sig n) where+  -- Without a specialisation, each node is forced through dictionaries built+  -- from the quantified constraints. The specialised rnf calls itself on the+  -- subterms and inlines the signature's rnf.+  {-# INLINABLE rnf #-}+  rnf (Var name)  = rnf name+  rnf (Node node) = rnf node++instance GenericK (ScopedAST binder sig) where+  type RepK (ScopedAST binder sig) =+    Exists Foil.S+      (Field (Kon binder :@: Var1 :@: Var0) :*: Field (Kon AST :@: Kon binder :@: Kon sig :@: Var0))+  toK (Kind.Exists (Kind.Field binder Kind.:*: Kind.Field ast)) = ScopedAST binder ast+  fromK (ScopedAST binder ast) = Kind.Exists (Kind.Field binder Kind.:*: Kind.Field ast)++instance GenericK (AST binder sig) where+  type RepK (AST binder sig) =+    Field (Foil.Name :$: Var0)+    :+: Field (sig+                :$: (Kon ScopedAST :@: Kon binder :@: Kon sig :@: Var0)+                :@: (Kon AST :@: Kon binder :@: Kon sig :@: Var0))++instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.Sinkable (ScopedAST binder sig)+instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.Sinkable (AST binder sig)++instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.SinkableK (ScopedAST binder sig)+instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder) => Foil.SinkableK (AST binder sig)++instance Foil.InjectName (AST binder sig) where+  injectName = Var++-- * Substitution++-- | Substitution for free (scoped monads).+--+-- @since 0.0.1+{-# INLINE substitute #-}+substitute+  :: forall sig binder o i. (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder, Foil.SinkableK binder)+  => Foil.Scope o+  -> Foil.Substitution (AST binder sig) i o+  -> AST binder sig i+  -> AST binder sig o+substitute = goSubst+  where+    -- The loop is local and takes the instances from 'substitute', so once+    -- 'substitute' is inlined at a known signature, the loop uses that+    -- signature's instances directly.+    goSubst+      :: Foil.Distinct o'+      => Foil.Scope o'+      -> Foil.Substitution (AST binder sig) i' o'+      -> AST binder sig i'+      -> AST binder sig o'+    goSubst scope subst term+      -- An empty substitution maps every name to itself ('addRename' deletes+      -- identity renames), so the result is the very term, and the coercion is+      -- the one 'Control.Monad.Foil.sink' performs. Substitutions go empty often: opening a+      -- scoped term with its own binder's name is an identity rename, and under+      -- a deterministic allocation policy a refreshed binder usually keeps its+      -- name, deleting its entry. Binders that shadow the ambient scope are+      -- left as they stand, as on the no-clash path below. A caller that wants+      -- them refreshed asks 'substituteRefreshed'.+      | Foil.nullSubst subst = unsafeCoerce term+      | otherwise = go term+      where+        -- The substitution is known non-empty here, and it can only change+        -- under a binder, so the walk between binders is unchecked and each+        -- binder entry re-enters 'goSubst', testing emptiness exactly once.+        go = \case+          Var name -> Foil.lookupSubst subst name+          Node node -> Node (bimap f go node)+        f (ScopedAST binder body) =+          Foil.withRefreshedPattern scope binder $ \extendSubst binder' scope' ->+            let subst' = extendSubst (Foil.sink subst)+                body' = goSubst scope' subst' body+            in ScopedAST binder' body'++-- | A version of 'substitute' that refreshes every binder of the input term.+--+-- The substituted terms are inserted as they are, so their binders are not+-- refreshed. Apply 'refreshAST' to the result to normalize all of its binders.+--+-- In general, 'substitute' is more efficient since it does not always refresh binders.+--+-- @since 0.0.3+{-# INLINABLE substituteRefreshed #-}+substituteRefreshed+  :: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder, Foil.SinkableK binder)+  => Foil.Scope o+  -> Foil.Substitution (AST binder sig) i o+  -> AST binder sig i+  -> AST binder 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.withFreshPattern scope binder $ \extendSubst binder' scope' ->+        let subst' = extendSubst (Foil.sink subst)+            body' = substituteRefreshed scope' subst' body+        in ScopedAST binder' body'++-- | @'AST' binder sig@ is a monad relative to t'Control.Monad.Foil.Name'.+instance (Bifunctor sig, Foil.CoSinkable binder, Foil.SinkableK binder)+  => Foil.RelMonad Foil.Name (AST binder sig) where+  rreturn = Var+  rbind scope term subst =+    case term of+      Var name  -> subst name+      Node node -> Node (bimap g' g node)+    where+      g x = Foil.rbind scope x subst+      g' (ScopedAST binder body) =+        Foil.withRefreshedPattern' scope binder $ \extendSubst binder' scope' ->+          let subst' = extendSubst subst+           in ScopedAST binder' (Foil.rbind scope' body subst')++-- | Substitute terms for the names a pattern binds in a body, and apply an+-- environment to the body's other names.+--+-- @since 0.2.0+{-# INLINABLE substitutePattern #-}+substitutePattern+  :: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder', Foil.CoSinkable binder, Foil.SinkableK binder)+  => Foil.Scope o                           -- ^ Resulting scope.+  -> Foil.Substitution (AST binder sig) n o -- ^ Environment mapping names in scope @n@.+  -> binder' n i                            -- ^ Binders that extend scope @n@ to scope @i@.+  -> [AST binder sig o]                     -- ^ Terms for the bound names, in binding order ('error' if too few, extras ignored).+  -> AST binder sig i+  -> AST binder sig o+substitutePattern scope env binders args body =+  substitute scope env' body+  where+    env' = Foil.addSubstPattern env binders args++-- * Transformations between signatures++-- | Fold a term bottom-up into a scope-indexed carrier @r@. A variable goes+-- through the first function, a scoped subterm through the second, given its+-- binder and the fold of its body, and a node through the third, once its+-- subterms are folded. The scope indices stay as they are, so the carrier+-- can be another kind of term, as in 'hoistAST', or anything indexed by the+-- scope, such as @'Const' Int@ for a node count.+--+-- To change the binder type as well, map it in the second function:+--+-- > foldAST Var (\binder body -> ScopedAST (toBinder binder) body) Node+--+-- @since 0.6.0+{-# INLINABLE foldAST #-}+foldAST+  :: forall binder sig r s n. Bifunctor sig+  => (forall m. Foil.Name m -> r m)             -- ^ A variable.+  -> (forall m l. binder m l -> r l -> s m)     -- ^ A scoped subterm, from its binder and the fold of its body.+  -> (forall m. sig (s m) (r m) -> r m)         -- ^ A node, its subterms folded.+  -> AST binder sig n+  -> r n+foldAST var scoped node = go+  where+    go :: forall m. AST binder sig m -> r m+    go (Var x)  = var x+    go (Node t) = node (bimap (\(ScopedAST binder body) -> scoped binder (go body)) go t)++-- | Change the signature of a term by a natural transformation of+-- signatures, keeping the binders, as @hoistFree@ of the free package does+-- for a free monad. Each node is rebuilt, so this costs a traversal.+--+-- @since 0.6.0+{-# INLINABLE hoistAST #-}+hoistAST+  :: Bifunctor sig+  => (forall a b. sig a b -> sig' a b)+  -> AST binder sig n+  -> AST binder sig' n+hoistAST f = foldAST Var ScopedAST (Node . f)++-- * \(\alpha\)-equivalence++-- | Refresh (force) all binders in a term, minimizing the used indices.+--+-- @since 0.0.3+{-# INLINABLE refreshAST #-}+refreshAST+  :: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder, Foil.SinkableK binder)+  => Foil.Scope n+  -> AST binder sig n+  -> AST binder sig n+refreshAST scope = \case+  t@Var{} -> t+  Node t -> Node (bimap (refreshScopedAST scope) (refreshAST scope) t)++-- | Similar to `refreshAST`, but for scoped terms.+--+-- @since 0.0.3+{-# INLINABLE refreshScopedAST #-}+refreshScopedAST :: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder, Foil.SinkableK binder)+  => Foil.Scope n+  -> ScopedAST binder sig n+  -> ScopedAST binder sig n+refreshScopedAST scope (ScopedAST binder body) =+  Foil.withFreshPattern scope binder $ \extendSubst binder' scope' ->+    let subst = extendSubst (Foil.sink Foil.identitySubst)+    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 renames every binder on both sides+-- unconditionally, so it does strictly more work. It remains as an+-- independent implementation of the same test. Like 'alphaEquiv', it compares+-- patterns with 'Control.Monad.Foil.unifyPatternsIn', so the payloads of a+-- pattern are compared as well.+--+-- @since 0.0.3+{-# INLINABLE alphaEquivRefreshed #-}+alphaEquivRefreshed+  :: (Bifunctor sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)+  => Foil.Scope n+  -> AST binder sig n+  -> AST binder sig n+  -> Bool+alphaEquivRefreshed scope t1 t2 =+  unsafeEqASTIn scope (refreshAST scope t1) (refreshAST scope t2)++-- | A term is a scope-indexed value that can be compared up to α, which is what+-- a pattern carrying terms as payloads needs of them.+instance (ZipMatchK sig, Foil.UnifiablePattern binder)+    => Foil.AlphaEquiv (AST binder sig) where+  alphaEquivIn = alphaEquiv++-- | \(\alpha\)-equivalence check for two terms in one scope+-- via unification of bound variables (via 'unifyNameBinders').+--+-- When two matching binders coincide, comparison continues with no work at+-- all. When they differ, the prescribed renaming is /threaded down the+-- recursion/ (see 'alphaEquivEnv') and consulted at variables only, so+-- nothing is ever copied. Applying the renaming eagerly instead would+-- materialise a renamed copy of the remaining body at every mismatched+-- binder, which is quadratic on a chain of them.+--+-- @since 0.0.3+{-# INLINE alphaEquiv #-}+alphaEquiv+  :: (ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder)+  => Foil.Scope n+  -> AST binder sig n+  -> AST binder sig n+  -> Bool+alphaEquiv = aeAlphaEquiv alphaEquivLoops++-- | Same as 'alphaEquiv' but for scoped terms.+--+-- While the binders of the two sides coincide, this runs with no renaming+-- state at all. The first pair that differs switches to 'alphaEquivEnv',+-- which threads the renamings down and switches back when they empty out.+--+-- @since 0.0.3+{-# INLINE alphaEquivScoped #-}+alphaEquivScoped+  :: forall sig binder n. (ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder)+  => Foil.Scope n+  -> ScopedAST binder sig n+  -> ScopedAST binder sig n+  -> Bool+alphaEquivScoped = aeAlphaEquivScoped alphaEquivLoops++-- | The raw name a verdict's renaming assigns to a pattern's name.+--+-- @since 0.4.0+renamedId :: (Foil.NameBinder n a -> Foil.NameBinder n b) -> Foil.Name a -> Int+renamedId rename = Foil.nameId . Foil.nameOf . rename . Foil.UnsafeNameBinder++-- | Bind the paired names of a binder pair. A pair whose names coincide+-- shadows both sides identically and is deleted from the environments. A pair+-- whose names differ binds both to one fresh level. Continues with+-- 'alphaEquivEnv' on the bodies.+--+-- @since 0.4.0+{-# INLINE bindPairs #-}+bindPairs+  :: forall sig binder m l1 l2. (ZipMatchK sig, Foil.Distinct m, Foil.UnifiablePattern binder)+  => Int -> IntMap Int -> IntMap Int -> [(Int, Int)]+  -> Foil.Scope m+  -> AST binder sig l1 -> AST binder sig l2 -> Bool+bindPairs = aeBindPairs alphaEquivLoops++-- | The renaming-threading worker behind 'alphaEquiv': compare two terms+-- under partial renamings of their names into shared /levels/.+--+-- Each environment maps a raw name to the level of the binder pair that bound+-- it on the comparison path, and a name outside its environment stands for+-- itself. A variable occurrence then compares as a level against a level, or+-- as a raw name against a raw name, and the two can never be conflated. This+-- is what makes threading sound where applying a raw renaming at the variables+-- would not be, since a renamed name could collide with one that passes+-- through unchanged and happens to share the target spelling. Levels are also+-- why no capture check is needed: a level is never a name, so there is nothing+-- for a binder to capture.+--+-- A binder pair whose names coincide /deletes/ those names from both+-- environments, the pair shadowing both sides identically. When the+-- environments empty out the comparison drops back to the stateless+-- 'alphaEquiv', so only the region of the terms below a mismatched binder,+-- and above the point where the mismatch is shadowed away, pays for the+-- threading at all.+--+-- The indices of the two terms are deliberately independent, in the style+-- of 'unsafeEqAST': the terms are never renamed into a common scope, so+-- no common index exists to give them.+--+-- @since 0.4.0+{-# INLINE alphaEquivEnv #-}+alphaEquivEnv+  :: forall sig binder n n1 n2. (ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder)+  => Int          -- ^ Next fresh level.+  -> IntMap Int   -- ^ Left renaming: raw name to the level that bound it.+  -> IntMap Int   -- ^ Right renaming.+  -> Foil.Scope n -- ^ Scope along the unified path (consulted by 'Control.Monad.Foil.unifyPatternsIn').+  -> AST binder sig n1+  -> AST binder sig n2+  -> Bool+alphaEquivEnv = aeAlphaEquivEnv alphaEquivLoops++-- | The scoped half of 'alphaEquivEnv': get the verdict from+-- 'Control.Monad.Foil.unifyPatternsIn', extend the environments as it prescribes, and+-- recurse into the bodies as they stand.+--+-- @since 0.4.0+{-# INLINE alphaEquivScopedEnv #-}+alphaEquivScopedEnv+  :: forall sig binder n n1 n2. (ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder)+  => Int+  -> IntMap Int+  -> IntMap Int+  -> Foil.Scope n+  -> ScopedAST binder sig n1+  -> ScopedAST binder sig n2+  -> Bool+alphaEquivScopedEnv = aeAlphaEquivScopedEnv alphaEquivLoops++-- | The mutually recursive loops behind α-equivalence, one field per+-- function above.+--+-- @since 0.6.1+data AlphaEquivLoops binder sig = AlphaEquivLoops+  { aeAlphaEquiv :: forall n. Foil.Distinct n => Foil.Scope n -> AST binder sig n -> AST binder sig n -> Bool+  , aeAlphaEquivScoped :: forall n. Foil.Distinct n => Foil.Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool+  , aeBindPairs :: forall m l1 l2. Foil.Distinct m => Int -> IntMap Int -> IntMap Int -> [(Int, Int)] -> Foil.Scope m -> AST binder sig l1 -> AST binder sig l2 -> Bool+  , aeAlphaEquivEnv :: forall n n1 n2. Foil.Distinct n => Int -> IntMap Int -> IntMap Int -> Foil.Scope n -> AST binder sig n1 -> AST binder sig n2 -> Bool+  , aeAlphaEquivScopedEnv :: forall n n1 n2. Foil.Distinct n => Int -> IntMap Int -> IntMap Int -> Foil.Scope n -> ScopedAST binder sig n1 -> ScopedAST binder sig n2 -> Bool+  }++-- | The loops behind α-equivalence. They are local and take the instances+-- from this function, so once it is inlined at a known signature, they use+-- that signature's instances directly. A recursive function with class+-- constraints would instead be split by GHC into a wrapper and a worker+-- (some of its dictionaries, such as t'Control.Monad.Foil.Distinct', are never used at+-- runtime), and a client's @SPECIALIZE@ pragma would reach only the wrapper.+--+-- @since 0.6.1+{-# INLINE alphaEquivLoops #-}+alphaEquivLoops+  :: forall sig binder. (ZipMatchK sig, Foil.UnifiablePattern binder)+  => AlphaEquivLoops binder sig+alphaEquivLoops = AlphaEquivLoops goAlpha goScoped goPairs goEnv goScopedEnv+  where+    goAlpha+      :: Foil.Distinct n+      => Foil.Scope n+      -> AST binder sig n+      -> AST binder sig n+      -> Bool+    goAlpha _scope (Var x) (Var y) = x == y+    goAlpha scope (Node l) (Node r) =+      case zipMatchWith2 (unit . goScoped scope) (unit . goAlpha scope) l r of+        Nothing -> False+        Just _  -> True+      where+        unit f x = if f x then Just () else Nothing+    goAlpha _ _ _ = False++    goScoped+      :: forall n. Foil.Distinct n+      => Foil.Scope n+      -> ScopedAST binder sig n+      -> ScopedAST binder sig n+      -> Bool+    goScoped scope+      (ScopedAST binder1 body1)+      (ScopedAST binder2 body2) =+        case Foil.unifyPatternsIn scope binder1 binder2 of+          -- the binders coincide: compare the bodies as they stand+          Foil.SameNameBinders{} ->  -- after seeing this we know that body scopes are the same+            case Foil.assertDistinct binder1 of+              Foil.Distinct ->+                let scope1 = Foil.extendScopePattern binder1 scope+                in goAlpha scope1 body1 body2+          -- the left binder is renamed towards the right one+          Foil.RenameLeftNameBinder _ rename1to2 ->+            case Foil.assertDistinct binder2 of+              Foil.Distinct ->+                let scope2 = Foil.extendScopePattern binder2 scope+                    pairs = [ (Foil.nameId x, renamedId rename1to2 x)+                            | x <- Foil.namesOfPattern binder1 ]+                in enterEnv pairs scope2 body1 body2+          -- the right binder is renamed towards the left one+          Foil.RenameRightNameBinder _ rename2to1 ->+            case Foil.assertDistinct binder1 of+              Foil.Distinct ->+                let scope1 = Foil.extendScopePattern binder1 scope+                    pairs = [ (renamedId rename2to1 y, Foil.nameId y)+                            | y <- Foil.namesOfPattern binder2 ]+                in enterEnv pairs scope1 body1 body2+          -- both are renamed towards a unified pattern: pair the two sides'+          -- names through the unified name each maps to+          Foil.RenameBothBinders binder' rename1 rename2 ->+            case Foil.assertDistinct binder' of+              Foil.Distinct ->+                let scope' = Foil.extendScopePattern binder' scope+                    leftU = IntMap.fromList+                      [ (renamedId rename1 x, Foil.nameId x)+                      | x <- Foil.namesOfPattern binder1 ]+                    rightU = IntMap.fromList+                      [ (renamedId rename2 y, Foil.nameId y)+                      | y <- Foil.namesOfPattern binder2 ]+                    pairs = IntMap.elems (IntMap.intersectionWith (,) leftU rightU)+                in enterEnv pairs scope' body1 body2+          -- if we cannot unify patterns then scopes are not alpha-equivalent+          Foil.NotUnifiable -> False+      where+        enterEnv+          :: forall m l1 l2. Foil.Distinct m+          => [(Int, Int)] -> Foil.Scope m+          -> AST binder sig l1 -> AST binder sig l2 -> Bool+        enterEnv pairs scope' = goPairs 0 IntMap.empty IntMap.empty pairs scope'++    goPairs+      :: forall m l1 l2. Foil.Distinct m+      => Int -> IntMap Int -> IntMap Int -> [(Int, Int)]+      -> Foil.Scope m+      -> AST binder sig l1 -> AST binder sig l2 -> Bool+    goPairs lvl envL envR pairs scope body1 body2 = case pairs of+      [] -> goEnv lvl envL envR scope body1 body2+      ((x, y) : rest)+        | x == y    -> goPairs lvl (IntMap.delete x envL) (IntMap.delete y envR) rest scope body1 body2+        | otherwise -> goPairs (lvl + 1) (IntMap.insert x lvl envL) (IntMap.insert y lvl envR) rest scope body1 body2++    goEnv+      :: forall n n1 n2. Foil.Distinct n+      => Int          -- ^ Next fresh level.+      -> IntMap Int   -- ^ Left renaming: raw name to the level that bound it.+      -> IntMap Int   -- ^ Right renaming.+      -> Foil.Scope n -- ^ Scope along the unified path (consulted by 'Control.Monad.Foil.unifyPatternsIn').+      -> AST binder sig n1+      -> AST binder sig n2+      -> Bool+    goEnv lvl envL envR scope t1 t2+      | IntMap.null envL && IntMap.null envR =+          -- The renamings have emptied out (or never held anything): the+          -- terms coincide raw-for-raw from here on, so compare them where+          -- they stand. The coercion brings both indices to the scope's,+          -- which is the unified path the comparison speaks of.+          goAlpha scope (unsafeCoerce t1 :: AST binder sig n) (unsafeCoerce t2 :: AST binder sig n)+      | otherwise = case (t1, t2) of+          (Var x, Var y) ->+            case (IntMap.lookup (Foil.nameId x) envL, IntMap.lookup (Foil.nameId y) envR) of+              (Just k1, Just k2) -> k1 == k2+              (Nothing, Nothing) -> Foil.nameId x == Foil.nameId y+              _                  -> False+          (Node l, Node r) ->+            case zipMatchWith2+                   (unit . goScopedEnv lvl envL envR scope)+                   (unit . goEnv lvl envL envR scope) l r of+              Nothing -> False+              Just _  -> True+          _ -> False+      where+        unit f x = if f x then Just () else Nothing++    goScopedEnv+      :: forall n n1 n2. Foil.Distinct n+      => Int+      -> IntMap Int+      -> IntMap Int+      -> Foil.Scope n+      -> ScopedAST binder sig n1+      -> ScopedAST binder sig n2+      -> Bool+    goScopedEnv lvl envL envR scope scoped1 scoped2 =+      -- The scoped terms are compared where they stand; the coercion only+      -- brings their indices to the scope's, which is the unified path the+      -- environments and the verdicts speak of.+      case (unsafeCoerce scoped1 :: ScopedAST binder sig n, unsafeCoerce scoped2 :: ScopedAST binder sig n) of+        (ScopedAST binder1 body1, ScopedAST binder2 body2) ->+          case Foil.unifyPatternsIn scope binder1 binder2 of+            -- the binders coincide: the pair shadows both sides identically+            Foil.SameNameBinders{} ->+              case Foil.assertDistinct binder1 of+                Foil.Distinct ->+                  let scope' = Foil.extendScopePattern binder1 scope+                      names = map Foil.nameId (Foil.namesOfPattern binder1)+                      envL' = deleteAll names envL+                      envR' = deleteAll names envR+                   in goEnv lvl envL' envR' scope' body1 body2+            -- the left binder is renamed towards the right one+            Foil.RenameLeftNameBinder _ rename1to2 ->+              case Foil.assertDistinct binder2 of+                Foil.Distinct ->+                  let scope' = Foil.extendScopePattern binder2 scope+                      pairs = [ (Foil.nameId x, renamedId rename1to2 x)+                              | x <- Foil.namesOfPattern binder1 ]+                   in goPairs lvl envL envR pairs scope' body1 body2+            -- the right binder is renamed towards the left one+            Foil.RenameRightNameBinder _ rename2to1 ->+              case Foil.assertDistinct binder1 of+                Foil.Distinct ->+                  let scope' = Foil.extendScopePattern binder1 scope+                      pairs = [ (renamedId rename2to1 y, Foil.nameId y)+                              | y <- Foil.namesOfPattern binder2 ]+                   in goPairs lvl envL envR pairs scope' body1 body2+            -- both are renamed towards a unified pattern: pair the two sides'+            -- names through the unified name each maps to+            Foil.RenameBothBinders binder' rename1 rename2 ->+              case Foil.assertDistinct binder' of+                Foil.Distinct ->+                  let scope' = Foil.extendScopePattern binder' scope+                      leftU = IntMap.fromList+                        [ (renamedId rename1 x, Foil.nameId x)+                        | x <- Foil.namesOfPattern binder1 ]+                      rightU = IntMap.fromList+                        [ (renamedId rename2 y, Foil.nameId y)+                        | y <- Foil.namesOfPattern binder2 ]+                      pairs = IntMap.elems (IntMap.intersectionWith (,) leftU rightU)+                   in goPairs lvl envL envR pairs scope' body1 body2+            Foil.NotUnifiable -> False+      where+        deleteAll names env = case names of+          []       -> env+          (i : is) -> deleteAll is (IntMap.delete i env)++-- ** Unsafe equality checks++-- | /Unsafe/ equality check for two terms. Names are compared by their raw+-- identifiers, and patterns by 'Control.Monad.Foil.Internal.unsafeEqPattern', which uses the+-- binder-only 'Control.Monad.Foil.unifyPatterns'. Thus α-equivalent terms whose binders+-- differ compare unequal, so normalize both sides with 'refreshAST' first.+--+-- @since 0.0.3+{-# INLINE unsafeEqAST #-}+unsafeEqAST+  :: (ZipMatchK sig, Foil.UnifiablePattern binder, Foil.Distinct n, Foil.Distinct l)+  => AST binder sig n+  -> AST binder sig l+  -> Bool+unsafeEqAST = eqAST unsafeEqLoops++-- | A version of 'unsafeEqAST' for scoped terms.+--+-- @since 0.0.3+{-# INLINE unsafeEqScopedAST #-}+unsafeEqScopedAST+  :: (ZipMatchK sig, Foil.UnifiablePattern binder, Foil.Distinct n, Foil.Distinct l)+  => ScopedAST binder sig n+  -> ScopedAST binder sig l+  -> Bool+unsafeEqScopedAST = eqScopedAST unsafeEqLoops++-- | The two mutually recursive loops behind 'unsafeEqAST' and+-- 'unsafeEqScopedAST'.+--+-- @since 0.6.1+data EqLoops binder sig = EqLoops+  { eqAST       :: forall n l. (Foil.Distinct n, Foil.Distinct l) => AST binder sig n -> AST binder sig l -> Bool+  , eqScopedAST :: forall n l. (Foil.Distinct n, Foil.Distinct l) => ScopedAST binder sig n -> ScopedAST binder sig l -> Bool+  }++-- | The loops behind 'unsafeEqAST' and 'unsafeEqScopedAST', local for the+-- same reason as those of 'alphaEquivLoops'.+--+-- @since 0.6.1+{-# INLINE unsafeEqLoops #-}+unsafeEqLoops+  :: forall sig binder. (ZipMatchK sig, Foil.UnifiablePattern binder)+  => EqLoops binder sig+unsafeEqLoops = EqLoops goAST goScoped+  where+    goAST :: forall n l. (Foil.Distinct n, Foil.Distinct l) => AST binder sig n -> AST binder sig l -> Bool+    goAST (Var x) (Var y) = Foil.nameId x == Foil.nameId y+    goAST (Node t1) (Node t2) =+      case zipMatchWith2 (unit . goScoped) (unit . goAST) t1 t2 of+        Nothing -> False+        Just _  -> True+    goAST _ _ = False++    goScoped :: forall n l. (Foil.Distinct n, Foil.Distinct l) => ScopedAST binder sig n -> ScopedAST binder sig l -> Bool+    goScoped (ScopedAST binder1 body1) (ScopedAST binder2 body2) = and+      [ Foil.unsafeEqPattern binder1 binder2+      , case (Foil.assertDistinct binder1, Foil.assertDistinct binder2) of+          (Foil.Distinct, Foil.Distinct) -> body1 `goAST` body2+      ]++    unit f x = if f x then Just () else Nothing++-- | /Unsafe/ equality check for two terms in one scope. Names are compared+-- directly, and patterns by 'Control.Monad.Foil.unifyPatternsIn', which must+-- find their binders the same. Thus α-equivalent terms whose binders differ+-- compare unequal, so normalize both sides with 'refreshAST' first, as+-- 'alphaEquivRefreshed' does.+--+-- Unlike 'unsafeEqAST', this compares the payloads of patterns, which+-- 'Control.Monad.Foil.unifyPatternsIn' needs the scope for.+--+-- @since 0.6.1+{-# INLINE unsafeEqASTIn #-}+unsafeEqASTIn+  :: (ZipMatchK sig, Foil.UnifiablePattern binder, Foil.Distinct n)+  => Foil.Scope n+  -> AST binder sig n+  -> AST binder sig n+  -> Bool+unsafeEqASTIn = eqASTIn unsafeEqInLoops++-- | A version of 'unsafeEqASTIn' for scoped terms.+--+-- @since 0.6.1+{-# INLINE unsafeEqScopedASTIn #-}+unsafeEqScopedASTIn+  :: (ZipMatchK sig, Foil.UnifiablePattern binder, Foil.Distinct n)+  => Foil.Scope n+  -> ScopedAST binder sig n+  -> ScopedAST binder sig n+  -> Bool+unsafeEqScopedASTIn = eqScopedASTIn unsafeEqInLoops++-- | The two mutually recursive loops behind 'unsafeEqASTIn' and+-- 'unsafeEqScopedASTIn'.+--+-- @since 0.6.1+data EqInLoops binder sig = EqInLoops+  { eqASTIn       :: forall n. Foil.Distinct n => Foil.Scope n -> AST binder sig n -> AST binder sig n -> Bool+  , eqScopedASTIn :: forall n. Foil.Distinct n => Foil.Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool+  }++-- | The loops behind 'unsafeEqASTIn' and 'unsafeEqScopedASTIn', local for+-- the same reason as those of 'alphaEquivLoops'.+--+-- @since 0.6.1+{-# INLINE unsafeEqInLoops #-}+unsafeEqInLoops+  :: forall sig binder. (ZipMatchK sig, Foil.UnifiablePattern binder)+  => EqInLoops binder sig+unsafeEqInLoops = EqInLoops goAST goScoped+  where+    goAST :: forall n. Foil.Distinct n => Foil.Scope n -> AST binder sig n -> AST binder sig n -> Bool+    goAST _ (Var x) (Var y) = x == y+    goAST scope (Node t1) (Node t2) =+      case zipMatchWith2 (unit . goScoped scope) (unit . goAST scope) t1 t2 of+        Nothing -> False+        Just _  -> True+    goAST _ _ _ = False++    goScoped :: forall n. Foil.Distinct n => Foil.Scope n -> ScopedAST binder sig n -> ScopedAST binder sig n -> Bool+    goScoped scope (ScopedAST binder1 body1) (ScopedAST binder2 body2) =+      case Foil.unifyPatternsIn scope binder1 binder2 of+        Foil.SameNameBinders{} -> case Foil.assertDistinct binder1 of+          Foil.Distinct -> goAST (Foil.extendScopePattern binder1 scope) body1 body2+        _ -> False++    unit f x = if f x then Just () else Nothing++-- * Converting to and from free foil++-- ** Convert to free foil++-- | An identifier a raw term mentions that the names given for conversion do+-- not account for.+--+-- Note what this does and does not carry. It cannot carry a position: the+-- conversion functions are generic in the raw term and only ever see it through+-- @toSig@, so a source location, if the syntax has one, is not theirs to read.+-- What they do know, and a caller checking names beforehand does not, is which+-- names were in scope /at the occurrence/, the binders passed on the way down+-- included. That is what a \"did you mean\" needs.+--+-- @since 0.4.0+data UnresolvedName rawIdent = UnresolvedName+  { unresolvedIdent   :: rawIdent+    -- ^ The identifier that did not resolve.+  , unresolvedInScope :: [rawIdent]+    -- ^ What was in scope where it occurred.+  } deriving (Eq, Show, Functor, Foldable, Traversable)++-- | The identifiers a raw term mentions that a set of names cannot resolve, in+-- the order they occur.+--+-- This is 'unsafeConvertToAST' with the conversion left out, so it descends+-- under binders in the same way and accounts for what they bind. A caller+-- that converts with 'tryConvertToASTWith' should use 'unresolvedNamesWith'+-- instead, so that its constants do not count as unresolved.+--+-- @since 0.4.0+unresolvedNames+  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.+     (Foil.Distinct n, Bifoldable sig, Ord rawIdent, Foil.CoSinkable binder)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -- ^ Unpeel one syntax node (or a variable) from a raw term.+  -> (forall x z. Foil.Distinct x+      => Foil.Scope x+      -> Map rawIdent (Foil.Name x)+      -> rawPattern+      -> (forall y. Foil.DExt x y+          => binder x y+          -> Map rawIdent (Foil.Name y)+          -> z)+      -> z)+  -- ^ Convert raw pattern into a scope-safe pattern.+  -> (rawScopedTerm -> rawTerm)+  -- ^ Extract a term from a scoped term (or crash).+  -> Foil.Scope n+  -- ^ Resulting scope of the constructed term.+  -> Map rawIdent (Foil.Name n)+  -- ^ Known names of free variables in scope @n@.+  -> rawTerm+  -- ^ Raw term.+  -> [UnresolvedName rawIdent]+unresolvedNames toSig fromRawPattern getScopedTerm scope names =+  unresolvedNamesWith toSig fromRawPattern getScopedTerm scope names Map.empty++-- | The identifiers a raw term mentions that neither a set of names nor a+-- table of constants can resolve, in the order they occur. This is+-- 'unresolvedNames' for the arguments of 'tryConvertToASTWith': given the same+-- names and constants, it reports every identifier that+-- 'tryConvertToASTWith' would fail on, and the first of them is the one that+-- 'tryConvertToASTWith' reports.+--+-- @since 0.6.1+unresolvedNamesWith+  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.+     (Foil.Distinct n, Bifoldable sig, Ord rawIdent, Foil.CoSinkable binder)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -- ^ Unpeel one syntax node (or a variable) from a raw term.+  -> (forall x z. Foil.Distinct x+      => Foil.Scope x+      -> Map rawIdent (Foil.Name x)+      -> rawPattern+      -> (forall y. Foil.DExt x y+          => binder x y+          -> Map rawIdent (Foil.Name y)+          -> z)+      -> z)+  -- ^ Convert raw pattern into a scope-safe pattern.+  -> (rawScopedTerm -> rawTerm)+  -- ^ Extract a term from a scoped term (or crash).+  -> Foil.Scope n+  -- ^ Resulting scope of the constructed term.+  -> Map rawIdent (Foil.Name n)+  -- ^ Known names of free variables in scope @n@.+  -> Map rawIdent (AST binder sig n)+  -- ^ Identifiers that denote a term rather than a variable.+  -> rawTerm+  -- ^ Raw term.+  -> [UnresolvedName rawIdent]+unresolvedNamesWith toSig fromRawPattern getScopedTerm scope0 names0 terms =+  go scope0 names0+  where+    constants = Map.keys terms++    go :: forall x. Foil.Distinct x+       => Foil.Scope x -> Map rawIdent (Foil.Name x) -> rawTerm -> [UnresolvedName rawIdent]+    go scope names t = case toSig t of+      Left x+        | Map.member x names || Map.member x terms -> []+        | otherwise -> [UnresolvedName x (Map.keys names <> constants)]+      Right node -> bifoldMap (goScoped scope names) (go scope names) node++    goScoped :: forall x. Foil.Distinct x+             => Foil.Scope x -> Map rawIdent (Foil.Name x)+             -> (rawPattern, rawScopedTerm) -> [UnresolvedName rawIdent]+    goScoped scope names (pat, scopedTerm) =+      fromRawPattern scope names pat $ \binder' names' ->+        go (Foil.extendScopePattern binder' scope) names' (getScopedTerm scopedTerm)++-- | Convert a raw term into a scope-safe term, reporting the first identifier+-- that does not resolve.+--+-- One pass, short-circuiting at the first failure, so a term that resolves+-- costs no more than 'unsafeConvertToAST' does. The report is complete for+-- that one identifier, since 'unresolvedInScope' is built where the conversion+-- fails and is never computed on the way through.+--+-- A caller wanting /every/ unresolved identifier rather than the first pays a+-- second pass for it, with 'unresolvedNames'. The successful path stays fast+-- that way, and a failure can afford to be walked again for a better message.+--+-- @since 0.4.0+tryConvertToAST+  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.+     (Foil.Distinct n, Bitraversable sig, Ord rawIdent,+      Foil.CoSinkable binder, Foil.SinkableK binder)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -- ^ Unpeel one syntax node (or a variable) from a raw term.+  -> (forall x z. Foil.Distinct x+      => Foil.Scope x+      -> Map rawIdent (Foil.Name x)+      -> rawPattern+      -> (forall y. Foil.DExt x y+          => binder x y+          -> Map rawIdent (Foil.Name y)+          -> z)+      -> z)+  -- ^ Convert raw pattern into a scope-safe pattern.+  -> (rawScopedTerm -> rawTerm)+  -- ^ Extract a term from a scoped term (or crash).+  -> Foil.Scope n+  -- ^ Resulting scope of the constructed term.+  -> Map rawIdent (Foil.Name n)+  -- ^ Known names of free variables in scope @n@.+  -> rawTerm+  -- ^ Raw term.+  -> Either (UnresolvedName rawIdent) (AST binder sig n)+tryConvertToAST toSig fromRawPattern getScopedTerm scope names =+  tryConvertToASTWith toSig fromRawPattern getScopedTerm scope names Map.empty++-- | Convert a raw term into a scope-safe term, resolving some identifiers to+-- whole terms rather than to variables.+--+-- The extra table is what a language with /constants/ needs: an identifier that+-- denotes a top-level declaration, a primitive, or an abbreviation stands for+-- something that is not a variable, and resolving it during conversion is the+-- only place where the binders are known. Doing it in a pass of one's own means+-- writing a binder-aware traversal of the raw syntax by hand.+--+-- Variables win: the table of names is consulted first, so a binder shadows an+-- entry here, and an entry here shadows nothing. The table is sunk when going+-- under a binder, exactly as the names are, so its entries may mention anything+-- in scope where conversion started and need not be closed.+--+-- @since 0.4.0+tryConvertToASTWith+  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.+     (Foil.Distinct n, Bitraversable sig, Ord rawIdent,+      Foil.CoSinkable binder, Foil.SinkableK binder)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -- ^ Unpeel one syntax node (or a variable) from a raw term.+  -> (forall x z. Foil.Distinct x+      => Foil.Scope x+      -> Map rawIdent (Foil.Name x)+      -> rawPattern+      -> (forall y. Foil.DExt x y+          => binder x y+          -> Map rawIdent (Foil.Name y)+          -> z)+      -> z)+  -- ^ Convert raw pattern into a scope-safe pattern.+  -> (rawScopedTerm -> rawTerm)+  -- ^ Extract a term from a scoped term (or crash).+  -> Foil.Scope n+  -- ^ Resulting scope of the constructed term.+  -> Map rawIdent (Foil.Name n)+  -- ^ Known names of free variables in scope @n@.+  -> Map rawIdent (AST binder sig n)+  -- ^ Identifiers that denote a term rather than a variable.+  -> rawTerm+  -- ^ Raw term.+  -> Either (UnresolvedName rawIdent) (AST binder sig n)+tryConvertToASTWith toSig fromRawPattern getScopedTerm = go+  where+    go :: forall x. Foil.Distinct x+       => Foil.Scope x -> Map rawIdent (Foil.Name x)+       -> Map rawIdent (AST binder sig x) -> rawTerm+       -> Either (UnresolvedName rawIdent) (AST binder sig x)+    go scope names terms t = case toSig t of+      Left x -> case Map.lookup x names of+        Just name -> Right (Var name)+        Nothing   -> case Map.lookup x terms of+          Just term -> Right term+          Nothing   -> Left (UnresolvedName x (Map.keys names <> Map.keys terms))+      Right node ->+        Node <$> bitraverse (goScoped scope names terms) (go scope names terms) node++    goScoped :: forall x. Foil.Distinct x+             => Foil.Scope x -> Map rawIdent (Foil.Name x)+             -> Map rawIdent (AST binder sig x)+             -> (rawPattern, rawScopedTerm)+             -> Either (UnresolvedName rawIdent) (ScopedAST binder sig x)+    goScoped scope names terms (pat, scopedTerm) =+      fromRawPattern scope names pat $ \binder' names' ->+        ScopedAST binder'+          <$> go (Foil.extendScopePattern binder' scope) names'+                 (Foil.sink1 terms) (getScopedTerm scopedTerm)++-- | Convert a raw term into a scope-safe term, calling 'error' on an+-- identifier that does not resolve.+--+-- Prefer 'tryConvertToAST', which reports such identifiers. This is for callers+-- that have already established that every name resolves.+--+-- @since 0.4.0+unsafeConvertToAST+  :: (Foil.Distinct n, Bifunctor sig, Ord rawIdent, Foil.CoSinkable binder)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -- ^ Unpeel one syntax node (or a variable) from a raw term.+  -> (forall x z. Foil.Distinct x+      => Foil.Scope x+      -> Map rawIdent (Foil.Name x)+      -> rawPattern+      -> (forall y. Foil.DExt x y+          => binder x y+          -> Map rawIdent (Foil.Name y)+          -> z)+      -> z)+  -- ^ Convert raw pattern into a scope-safe pattern.+  -> (rawScopedTerm -> rawTerm)+  -- ^ Extract a term from a scoped term (or crash).+  -> Foil.Scope n+  -- ^ Resulting scope of the constructed term.+  -> Map rawIdent (Foil.Name n)+  -- ^ Known names of free variables in scope @n@.+  -> rawTerm+  -- ^ Raw term.+  -> AST binder sig n+unsafeConvertToAST toSig fromRawPattern 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+        (unsafeConvertToScopedAST toSig fromRawPattern getScopedTerm scope names)+        (unsafeConvertToAST toSig fromRawPattern getScopedTerm scope names)+        node++-- | Same as 'unsafeConvertToAST' but for scoped terms.+--+-- @since 0.4.0+unsafeConvertToScopedAST+  :: (Foil.Distinct n, Bifunctor sig, Ord rawIdent, Foil.CoSinkable binder)+  => (rawTerm -> Either rawIdent (sig (rawPattern, rawScopedTerm) rawTerm))+  -- ^ Unpeel one syntax node (or a variable) from a raw term.+  -> (forall x z. Foil.Distinct x+      => Foil.Scope x+      -> Map rawIdent (Foil.Name x)+      -> rawPattern+      -> (forall y. Foil.DExt x y+          => binder x y+          -> Map rawIdent (Foil.Name y)+          -> z)+      -> z)+  -- ^ Convert raw pattern into a scope-safe pattern.+  -> (rawScopedTerm -> rawTerm)+  -- ^ Extract a term from a scoped term (or crash).+  -> Foil.Scope n+  -- ^ Resulting scope of the constructed term.+  -> Map rawIdent (Foil.Name n)+  -- ^ Known names of free variables in scope @n@.+  -> (rawPattern, rawScopedTerm)+  -- ^ A pair of a pattern and a corresponding scoped term.+  -> ScopedAST binder sig n+unsafeConvertToScopedAST toSig fromRawPattern getScopedTerm scope names (pat, scopedTerm) =+  fromRawPattern scope names pat $ \binder' names' ->+    let scope' = Foil.extendScopePattern binder' scope+     in ScopedAST binder' (unsafeConvertToAST toSig fromRawPattern getScopedTerm scope' names' (getScopedTerm scopedTerm))++-- ** Convert from free foil++-- | Convert a scope-safe term back into a raw term.+-- Use 'convertFromASTWith' to name free variables separately from bound ones.+--+-- @since 0.0.3+convertFromAST+  :: Bifunctor sig+  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)+  -- ^ Peel back one layer of syntax.+  -> (rawIdent -> rawTerm)+  -- ^ Convert identifier into a raw variable term.+  -> (forall x y. binder x y -> rawPattern)+  -- ^ Convert scope-safe pattern into a raw pattern.+  -> (rawTerm -> rawScopedTerm)+  -- ^ Wrap raw term into a scoped term.+  -> (Int -> rawIdent)+  -- ^ Name a variable, bound or free, from its underlying integer identifier.+  -> AST binder sig n+  -- ^ Scope-safe term.+  -> 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++-- | Same as 'convertFromAST' but for scoped terms.+--+-- @since 0.0.3+convertFromScopedAST+  :: Bifunctor sig+  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)+  -- ^ Peel back one layer of syntax.+  -> (rawIdent -> rawTerm)+  -- ^ Convert identifier into a raw variable term.+  -> (forall x y. binder x y -> rawPattern)+  -- ^ Convert scope-safe pattern into a raw pattern.+  -> (rawTerm -> rawScopedTerm)+  -- ^ Wrap raw term into a scoped term.+  -> (Int -> rawIdent)+  -- ^ Name a variable, bound or free, from its underlying integer identifier.+  -> ScopedAST binder sig n+  -- ^ Scope-safe scoped term.+  -> (rawPattern, rawScopedTerm)+convertFromScopedAST fromSig fromVar makePattern makeScoped f = \case+  ScopedAST binder body ->+    ( makePattern binder+    , makeScoped (convertFromAST fromSig fromVar makePattern makeScoped f body))++-- | Convert a scope-safe term back into a raw term, naming the variables that+-- occur /free in the whole term/ separately from the bound ones.+--+-- 'convertFromAST' applies one naming function to every variable it meets,+-- bound or free, and gives it only a raw name. That is often not enough, since+-- raw names are not unique across scope indices: a binder inside a term may+-- share one with a name of the ambient scope, so naming by raw name alone can+-- print a bound variable as whatever the ambient scope calls that name.+--+-- Keeping the typed name is what distinguishes them, and 'Control.Monad.Foil.unsinkNamePattern'+-- is the operation for it: composing one per binder on the way down builds a+-- @t'Control.Monad.Foil.Name' x -> 'Maybe' ( t'Control.Monad.Foil.Name' n )@ that answers exactly the question.+--+-- @since 0.4.0+convertFromASTWith+  :: forall sig binder rawIdent rawTerm rawPattern rawScopedTerm n.+     (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder)+  => (sig (rawPattern, rawScopedTerm) rawTerm -> rawTerm)+  -- ^ Peel back one layer of syntax.+  -> (rawIdent -> rawTerm)+  -- ^ Convert identifier into a raw variable term.+  -> (forall x y. binder x y -> rawPattern)+  -- ^ Convert scope-safe pattern into a raw pattern.+  -> (rawTerm -> rawScopedTerm)+  -- ^ Wrap raw term into a scoped term.+  -> (Foil.Name n -> rawIdent)+  -- ^ Name a variable that is free in the whole term.+  -> (Int -> rawIdent)+  -- ^ Name a bound variable, from its underlying integer identifier.+  -> AST binder sig n+  -- ^ Scope-safe term.+  -> rawTerm+convertFromASTWith fromSig fromVar makePattern makeScoped freeName boundName =+    go Just+  where+    go :: forall x. Foil.Distinct x+       => (Foil.Name x -> Maybe (Foil.Name n)) -> AST binder sig x -> rawTerm+    go unsink = \case+      Var x -> fromVar $ case unsink x of+        Just name -> freeName name+        Nothing   -> boundName (Foil.nameId x)+      Node node -> fromSig (bimap (goScoped unsink) (go unsink) node)++    goScoped :: forall x. Foil.Distinct x+             => (Foil.Name x -> Maybe (Foil.Name n))+             -> ScopedAST binder sig x -> (rawPattern, rawScopedTerm)+    goScoped unsink (ScopedAST binder body) =+      case Foil.assertDistinct binder of+        Foil.Distinct ->+          ( makePattern binder+          , makeScoped+              (go (\name -> Foil.unsinkNamePattern binder name >>= unsink) body) )++-- ** Unsinking AST++-- | The support of a term: exactly the names that occur free in it.+--+-- This is the annotation that co-de-Bruijn syntax carries intrinsically and+-- that the foil does not need for weakening, since 'Control.Monad.Foil.sink' is a coercion.+-- Computing it traverses the whole term, so a client that restricts often+-- should cache it.+--+-- @since 0.4.0+supportOf+  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)+  => AST binder sig n -> Foil.NameSet n+supportOf = \case+  Var name  -> Foil.nameSetSingleton name+  Node node -> bifoldMap supportOfScopedAST supportOf node++-- | The support of a scoped term, in the scope /outside/ its binder.+--+-- @since 0.4.0+supportOfScopedAST+  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)+  => ScopedAST binder sig n -> Foil.NameSet n+supportOfScopedAST (ScopedAST binder body) =+  case Foil.assertDistinct binder of+    Foil.Distinct -> Foil.unsinkNameSet binder (supportOf body)++-- | Cut a term down to the scope of exactly the names it uses.+--+-- The term's support is computed with 'supportOf', as in 'unsinkAST', but+-- nothing is tested and nothing can fail, since the term inhabits the+-- restricted scope by construction. @t'Control.Monad.Foil.Ext' m n@ comes back with it, so+-- the term can be 'Control.Monad.Foil.sink'ed to where it came from for free.+--+-- Verifying a declared dependency, such as a @uses@ clause or a module's+-- parameters, is this plus a comparison: compute the scope a term really+-- inhabits, and check the declared one against it.+--+-- @since 0.4.0+withRelevantScope+  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)+  => AST binder sig n+  -> (forall m. (Foil.Ext m n, Foil.Distinct m)+      => Foil.Scope m -> AST binder sig m -> r)+  -> r+withRelevantScope term cont =+  Foil.withRestrictedScope (supportOf term) $ \scope ->+    cont scope (unsafeCoerce term)++-- | Restrict a term from scope @l@ to a smaller scope @n@, if every name that+-- occurs free in it is in @n@.+--+-- The scope @l@ must extend @n@, since names are compared by their raw+-- identifiers, which identify a name only within one chain of scope+-- extensions. On success the term is returned unchanged, since restricting a+-- term that does inhabit @n@ is a coercion.+--+-- @since 0.3.0+unsinkAST+  :: (Foil.DExt n l, Foil.CoSinkable binder, Bifoldable sig)+  => Foil.Scope n -> AST binder sig l -> Maybe (AST binder sig n)+unsinkAST scope term+  | Foil.nameSetSubsetOfScope (supportOf term) scope = Just (unsafeCoerce term)+  | otherwise = Nothing++-- | Get the free variables of an AST.+--+-- These come from 'supportOf', so they are distinct and in ascending order of+-- their identifiers.+--+-- @since 0.3.0+freeVarsOf+  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)+  => AST binder sig n -> [Foil.Name n]+freeVarsOf = Foil.nameSetToList . supportOf++-- | Get the free variables of a scoped AST, in the scope outside its binder.+--+-- @since 0.3.0+freeVarsOfScopedAST+  :: (Foil.Distinct n, Foil.CoSinkable binder, Bifoldable sig)+  => ScopedAST binder sig n -> [Foil.Name n]+freeVarsOfScopedAST = Foil.nameSetToList . supportOfScopedAST
src/Control/Monad/Free/Foil/TH/MkFreeFoil.hs view
@@ -81,21 +81,21 @@ data FreeFoilTermConfig = FreeFoilTermConfig   { rawIdentName          :: Name     -- ^ The type name for the identifiers.-    -- When identifiers occur in a term, they are converted to 'Foil.Name' (with an appropriate type-level scope parameter).-    -- When identifiers occur in a pattern, they are converted to 'Foil.NameBinder' (with appropriate type-level scope parameters).+    -- When identifiers occur in a term, they are converted to t'Control.Monad.Foil.Name' (with an appropriate type-level scope parameter).+    -- When identifiers occur in a pattern, they are converted to t'Control.Monad.Foil.NameBinder' (with appropriate type-level scope parameters).   , rawTermName           :: Name     -- ^ The type name for the term.-    -- This will be the main recursive type to be converted into an 'Foil.AST'.+    -- This will be the main recursive type to be converted into an t'Control.Monad.Free.Foil.AST'.   , rawBindingName        :: Name     -- ^ The type name for the binders (patterns).-    -- This will be the main binder type to used in 'Foil.AST'-representation of the terms.+    -- This will be the main binder type to used in t'Control.Monad.Free.Foil.AST'-representation of the terms.   , rawScopeName          :: Name     -- ^ The type name for the scoped term.-    -- This will be replaced with either 'Foil.ScopedAST' (with outer scope) or 'Foil.AST' (with inner scope)+    -- This will be replaced with either t'Control.Monad.Free.Foil.ScopedAST' (with outer scope) or t'Control.Monad.Free.Foil.AST' (with inner scope)     -- depending on its occurrence in a regular (sub)term or some quantified syntax.   , rawVarConName         :: Name     -- ^ The constructor name for the variables in a term.-    -- This constructor will be replaced with the standard 'Foil.Var'.+    -- This constructor will be replaced with the standard 'Control.Monad.Free.Foil.Var'.     -- It is expected to have exactly one field of type 'rawIdentName'.   , rawSubTermNames       :: [Name]     -- ^ Type names for subterm syntax.@@ -136,7 +136,7 @@ toFreeFoilNameTo FreeFoilConfig{..} name = mkName (freeFoilConvertToName (nameBase name))  -- | The name of the range-parametric sibling of a generated definition--- (see 'Foil.withFreshIn'): @toPatternIn@ beside @toPattern@.+-- (see 'Control.Monad.Foil.withFreshIn'): @toPatternIn@ beside @toPattern@. toNameIn :: Name -> Name toNameIn name = mkName (nameBase name ++ "In") @@ -378,7 +378,7 @@ -- -- It is when there is exactly one constructor, declared without an explicit -- @forall@ or context, and that constructor has exactly one field, which binds:--- an identifier (it becomes a 'Foil.NameBinder') or a nested binding type. A+-- an identifier (it becomes a t'Control.Monad.Foil.NameBinder') or a nested binding type. A -- field that binds gives the constructor the result type @T ... o i@ with -- distinct scope variables, as a newtype requires; a field that binds nothing -- gives @T ... o o@, which only a GADT can express.@@ -399,7 +399,7 @@ -- | Is this raw field a binding (pattern) field? -- -- Such a field has no counterpart in the free foil node: the binder it stands for--- lives inside the 'Foil.ScopedAST' of each scoped child.+-- lives inside the t'Control.Monad.Free.Foil.ScopedAST' of each scoped child. isBindingField :: FreeFoilConfig -> Type -> Bool isBindingField FreeFoilConfig{..} = \case   PeelConT typeName _ | Just _ <- lookupBindingName typeName freeFoilTermConfigs -> True@@ -407,7 +407,7 @@  -- | Is this raw field a scoped-term field? ----- Such a field becomes a 'Foil.ScopedAST', which carries a binder of its own.+-- Such a field becomes a t'Control.Monad.Free.Foil.ScopedAST', which carries a binder of its own. isScopeField :: FreeFoilConfig -> Type -> Bool isScopeField FreeFoilConfig{..} = \case   PeelConT typeName _ | Just _ <- lookupScopeName typeName freeFoilTermConfigs -> True@@ -415,7 +415,7 @@  -- | What a raw binding constructor's field becomes in the scope-safe binding -- type. Mirrors the classification in 'toFreeFoilBindingCon': an identifier--- becomes a 'Foil.NameBinder' and threads the scope; a binding type becomes a+-- becomes a t'Control.Monad.Foil.NameBinder' and threads the scope; a binding type becomes a -- nested binding type and threads the scope; anything else is a payload that -- binds nothing. data BindingFieldSort = FieldBinder | FieldPattern | FieldPayload@@ -437,8 +437,8 @@ -- | Does this raw payload type mention anything that converts to a -- scope-indexed type in the binding type (a term, a scoped term, an -- identifier, or a nested binding under a type constructor)? Such a payload--- cannot be rebuilt by the generated 'Foil.CoSinkable' instance, rebuilding it--- at another scope being what 'Foil.transportPayload' exists for. Generation+-- cannot be rebuilt by the generated t'Control.Monad.Foil.CoSinkable' instance, rebuilding it+-- at another scope being what 'Control.Monad.Foil.transportPayload' exists for. Generation -- refuses such a payload, matching the GenericK-side refusal for derived -- patterns. mentionsScopeIndexed :: FreeFoilConfig -> Type -> Bool@@ -473,7 +473,7 @@       RecGadtC names types _retType -> [ (name, map (\(_, _, t) -> t) types) | name <- names ]       ForallC _ _ con -> go con --- | One 'Foil.coSinkabilityProof' clause for a generated binding constructor:+-- | One 'Control.Monad.Foil.coSinkabilityProof' clause for a generated binding constructor: -- -- > coSinkabilityProof rename (Con x1 x2 x3) cont = -- >   coSinkabilityProof rename x1 $ \rename' x1' ->@@ -481,7 +481,7 @@ -- >       cont rename'' (Con x1' x2' x3) -- -- Binder and nested-pattern fields thread the renaming left to right (each via--- its own 'Foil.CoSinkable' instance); payload fields pass through untouched.+-- its own t'Control.Monad.Foil.CoSinkable' instance); payload fields pass through untouched. mkCoSinkabilityProofClause :: FreeFoilConfig -> (Name, [Type]) -> Q Clause mkCoSinkabilityProofClause config (rawConName, rawFieldTypes) = do   let conName = toConName config rawConName@@ -508,7 +508,7 @@   body <- go rename (zip sorts xs) id   return (Clause [VarP rename, ConP conName [] (map VarP xs), VarP cont] (NormalB body) []) --- | One 'Foil.withPattern' clause for a generated binding constructor:+-- | One 'Control.Monad.Foil.withPattern' clause for a generated binding constructor: -- -- > withPattern withBinder unit_ comp_ scope (Con x1 x2 x3) cont = -- >   withBinder scope x1 $ \f1 x1' ->@@ -516,9 +516,9 @@ -- >     in withPattern withBinder unit_ comp_ scope' x2 $ \f2 x2' scope'' -> -- >          cont (comp_ f1 f2) (Con x1' x2' x3) scope'' ----- A 'Foil.NameBinder' field is processed with @withBinder@ directly and+-- A t'Control.Monad.Foil.NameBinder' field is processed with @withBinder@ directly and -- extends the ambient scope for the fields to its right; a nested binding--- field recurses through its own 'Foil.withPattern', which hands the extended+-- field recurses through its own 'Control.Monad.Foil.withPattern', which hands the extended -- scope to its continuation. Results compose left to right with @comp_@; the -- final continuation receives the scope after the whole constructor, and a -- constructor that binds nothing hands @unit_@ and the ambient scope over.@@ -574,13 +574,13 @@     [VarP withBinder, VarP unit_, VarP comp_, VarP scope, ConP conName [] (map VarP xs), VarP cont]     (NormalB body) []) --- | One 'Foil.nameBinderListOf' clause for a generated binding constructor:+-- | One 'Control.Monad.Foil.nameBinderListOf' clause for a generated binding constructor: -- -- > nameBinderListOf (Con x1 x2 _) = -- >   concatNameBinderLists (nameBinderListOf x1) (nameBinderListOf x2) -- -- The binder and nested-pattern fields are listed left to right, in the order--- in which the 'Foil.withPattern' clause of 'mkWithPatternClause' visits them,+-- in which the 'Control.Monad.Foil.withPattern' clause of 'mkWithPatternClause' visits them, -- and payload fields are skipped. A constructor that binds nothing lists no -- binders. mkNameBinderListOfClause :: FreeFoilConfig -> (Name, [Type]) -> Q Clause@@ -592,7 +592,7 @@         | isBindingFieldSort sort = VarP x         | otherwise               = WildP       -- A binder is consed on directly. Only a nested binding type's list is-      -- concatenated, since 'Foil.concatNameBinderLists' is recursive and+      -- concatenated, since 'Control.Monad.Foil.concatNameBinderLists' is recursive and       -- copies its first argument.       list = \case         [] -> ConE 'Foil.NameBinderListEmpty@@ -829,7 +829,7 @@ -- /arguments/ are, and the two used to disagree: -- -- * a raw binding (pattern) field contributes __no__ argument, since a binder---   in the free foil lives inside the 'Foil.ScopedAST' it binds, not beside it;+--   in the free foil lives inside the t'Control.Monad.Free.Foil.ScopedAST' it binds, not beside it; -- * a raw scoped-term field contributes __two__ arguments, a binder and a body; -- * every other field contributes one argument, as before. --@@ -1067,14 +1067,14 @@         (BindingNewtype, [newCon]) -> NewtypeD [] bindingName newParams Nothing newCon []         _                          -> DataD [] bindingName newParams Nothing newCons [] -    -- A concrete 'Foil.CoSinkable' instance for the generated binding type,+    -- A concrete t'Control.Monad.Foil.CoSinkable' instance for the generated binding type,     -- one clause per constructor, delegating to the fields' instances. The     -- GenericK default routes every binder operation through a generic     -- representation traversal, which costs a measurable constant per binder     -- at runtime (see issue #82); the concrete instance removes it, and a     -- client no longer declares (or hand-writes) the instance itself. The-    -- instance lists its binders directly in 'Foil.nameBinderListOf', since-    -- the default would go through the recursive 'Foil.withPattern'.+    -- instance lists its binders directly in 'Control.Monad.Foil.nameBinderListOf', since+    -- the default would go through the recursive 'Control.Monad.Foil.withPattern'.     mkPatternCoSinkable FreeFoilTermConfig{..} = do       (tvars, cons) <- reifyDataOrNewtype rawBindingName       let bindingName = toFreeFoilName config rawBindingName
src/Data/ZipMatchK.hs view
@@ -37,14 +37,14 @@ import           Data.Coerce (coerce) import           Data.Monoid (All (..), Any (..)) import           Generics.Kind-import Data.Bitraversable  import Data.ZipMatchK.Generic import Data.ZipMatchK.Mappings --- | Perform one level of equality testing for two values and pair up components using @(,)@:+-- | Perform one level of equality testing for two values and pair up+-- corresponding components using @(,)@, for every type parameter: ----- > zipMatchK = zipMatchWithK (\x y -> Just (,) :^: M0)+-- > zipMatchK = zipMatchWithK pairMappings -- -- @since 0.2.0 zipMatchK :: forall f as bs. (ZipMatchK f, PairMappings as bs) => f :@@: as -> f :@@: bs -> Maybe (f :@@: ZipLoT as bs)@@ -68,29 +68,23 @@  -- | 'zipMatchWithK' specialised to functors. ----- Note: 'Traversable' is a morally correct constraint here.--- -- @since 0.3.0 zipMatchWith1-  :: (Traversable f, ZipMatchK f)+  :: ZipMatchK f   => (a -> a' -> Maybe a'')   -> f a -> f a' -> Maybe (f a'') zipMatchWith1 f = zipMatchWithK (f :^: M0)  -- | 'zipMatchK' specialised to functors. ----- Note: 'Traversable' is a morally correct constraint here.--- -- @since 0.3.0-zipMatch1 :: (Traversable f, ZipMatchK f) => f a -> f a' -> Maybe (f (a, a'))+zipMatch1 :: ZipMatchK f => f a -> f a' -> Maybe (f (a, a')) zipMatch1 = zipMatchWith1 pairA -- | 'zipMatchWithK' specialised to bifunctors. ----- Note: 'Bitraversable' is a morally correct constraint here.--- -- @since 0.3.0 zipMatchWith2-  :: (Bitraversable f, ZipMatchK f)+  :: ZipMatchK f   => (a -> a' -> Maybe a'')   -> (b -> b' -> Maybe b'')   -> f a b -> f a' b' -> Maybe (f a'' b'')@@ -98,10 +92,8 @@  -- | 'zipMatchK' specialised to bifunctors. ----- Note: 'Bitraversable' is a morally correct constraint here.--- -- @since 0.3.0-zipMatch2 :: (Bitraversable f, ZipMatchK f) => f a b -> f a' b' -> Maybe (f (a, a') (b, b'))+zipMatch2 :: ZipMatchK f => f a b -> f a' b' -> Maybe (f (a, a') (b, b')) zipMatch2 = zipMatchWith2 pairA pairA  -- | 'zipMatchFoldK' specialised to functors.
test/Control/Monad/Free/Foil/AlphaEquivSpec.hs view
@@ -1,10 +1,13 @@ {-# LANGUAGE DataKinds           #-} {-# LANGUAGE DeriveTraversable   #-}+{-# LANGUAGE FlexibleContexts    #-} {-# LANGUAGE GADTs               #-}+{-# LANGUAGE KindSignatures      #-} {-# LANGUAGE LambdaCase          #-} {-# LANGUAGE RankNTypes          #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell     #-}+{-# LANGUAGE TypeFamilies        #-}  -- | Properties of 'alphaEquiv' with threaded renamings. --@@ -28,6 +31,7 @@                                               unsafeAssertFresh) import           Control.Monad.Free.Foil import           Data.ZipMatchK.TH           (deriveZipMatchK)+import           Generics.Kind.TH            (deriveGenericK)  data LamSig scope term   = App term term@@ -41,6 +45,48 @@  type Term = AST Foil.NameBinder LamSig +-- | A binder annotated with a name of the outer scope, as a binder may be+-- annotated with its type. Only 'Foil.unifyPatternsIn' compares the+-- annotations, as it does the payloads of a telescope.+data AnnBinder (n :: Foil.S) (l :: Foil.S) where+  AnnBinder :: Foil.Name n -> Foil.NameBinder n l -> AnnBinder n l++deriveGenericK ''AnnBinder+instance Foil.SinkableK AnnBinder++-- | The annotation is a payload, so it is moved by hand (see+-- 'Foil.transportPayload').+instance Foil.CoSinkable AnnBinder where+  coSinkabilityProof rename (AnnBinder ann binder) cont =+    Foil.coSinkabilityProof rename binder $ \rename' binder' ->+      cont rename' (AnnBinder (rename ann) binder')+  withPattern withBinder _unit _comp scope (AnnBinder ann binder) cont =+    withBinder scope binder $ \fbinder binder' ->+      cont fbinder+        (AnnBinder (Foil.transportName Foil.verbatimTransport ann) binder')+        (Foil.extendScope binder' scope)+instance Foil.UnifiablePattern AnnBinder where+  unifyPatternsIn _scope l@(AnnBinder x _) r@(AnnBinder y _)+    | x == y    = Foil.unifyPatterns l r+    | otherwise = Foil.NotUnifiable++type AnnTerm = AST AnnBinder LamSig++-- | λ(x : ann). x+annId :: Foil.Distinct n => Foil.Scope n -> Foil.Name n -> AnnTerm n+annId scope ann = Foil.withFresh scope $ \x ->+  Node (Lam (ScopedAST (AnnBinder ann x) (Var (Foil.nameOf x))))++-- | Run a continuation in a scope with two names.+withTwoNames+  :: (forall n. Foil.Distinct n => Foil.Scope n -> Foil.Name n -> Foil.Name n -> r)+  -> r+withTwoNames cont =+  Foil.withFresh Foil.emptyScope $ \a ->+    let scope1 = Foil.extendScope a Foil.emptyScope+     in Foil.withFresh scope1 $ \b ->+          cont (Foil.extendScope b scope1) (Foil.sink (Foil.nameOf a)) (Foil.nameOf b)+ -- * Hand-built terms with chosen binder names  -- | A binder with a chosen raw name — either fresh for the scope, or@@ -121,7 +167,7 @@ renderShifted :: Int -> Skel -> Term Foil.VoidS renderShifted k s = go k Foil.emptyScope   where-    go :: Foil.Distinct n => Int -> Foil.Scope n -> Term Foil.VoidS+    go :: (Foil.Distinct n, Foil.Ext Foil.VoidS n) => Int -> Foil.Scope n -> Term Foil.VoidS     go 0 scope =       fromMaybe (error "the term uses a dummy binder")         (unsinkAST Foil.emptyScope (renderIn scope [] (case s of SLam{} -> s; _ -> SLam s)))@@ -159,6 +205,17 @@       let t1 = lam 7 (\_a -> lam 5 (\b -> Var b))           t2 = lam 5 (\_a -> lam 5 (\b -> Var b))       alphaEquiv Foil.emptyScope t1 t2 `shouldBe` True++  describe "binders with annotations" $ do+    it "rejects λ(x : a). x against λ(x : b). x" $+      withTwoNames $ \scope a b ->+        alphaEquiv scope (annId scope a) (annId scope b) `shouldBe` False+    it "rejects λ(x : a). x against λ(x : b). x with alphaEquivRefreshed" $+      withTwoNames $ \scope a b ->+        alphaEquivRefreshed scope (annId scope a) (annId scope b) `shouldBe` False+    it "accepts λ(x : a). x against λ(y : a). y with alphaEquivRefreshed" $+      withTwoNames $ \scope a _ ->+        alphaEquivRefreshed scope (annId scope a) (annId scope a) `shouldBe` True    describe "agreement with alphaEquivRefreshed" $ do     prop "on a term against its shifted rendering" $ \s (Positive k) ->
test/Control/Monad/Free/Foil/TH/MkFreeFoilSpec.hs view
@@ -17,7 +17,7 @@ import           Data.Bifunctor                                   (bimap) import           Data.Bitraversable                               (bitraverse) import           Data.Monoid                                      (Sum (..))-import           Data.ZipMatchK                                   (Mappings (..), ZipMatchK (..))+import           Data.ZipMatchK                                   (zipMatchWith2) import           Test.Hspec  import           Control.Monad.Free.Foil.TH.MkFreeFoilSpec.Config@@ -50,8 +50,8 @@     it "force a term and its patterns" $       rnf (toTerm Foil.emptyScope Map.empty (Let (PatternVar x) identity (ScopedTerm (Var x)))) `shouldBe` ()     it "match equal constructors and reject different ones" $ do-      zipMatchWithK (zipAdd :^: zipAdd :^: M0) (AppSig 1 2 :: TermSig Int Int) (AppSig 10 20) `shouldBe` Just (AppSig 11 22)-      zipMatchWithK (zipAdd :^: zipAdd :^: M0) (AppSig 1 2 :: TermSig Int Int) (FunSig 3) `shouldBe` Nothing+      zipMatchWith2 zipAdd zipAdd (AppSig 1 2 :: TermSig Int Int) (AppSig 10 20) `shouldBe` Just (AppSig 11 22)+      zipMatchWith2 zipAdd zipAdd (AppSig 1 2 :: TermSig Int Int) (FunSig 3) `shouldBe` Nothing    describe "a pattern adjacent to its scope (Fun)" $     it "round-trips, and the body still refers to the binder" $
+ test/Control/Monad/Free/Foil/UnresolvedNamesSpec.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE GADTs             #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE RankNTypes        #-}+{-# LANGUAGE TemplateHaskell   #-}++-- | 'unresolvedNames' and 'unresolvedNamesWith' on a small raw λ-calculus,+-- against what 'tryConvertToASTWith' reports.+module Control.Monad.Free.Foil.UnresolvedNamesSpec (spec) where++import           Data.Bifunctor.TH+import           Data.Map                (Map)+import qualified Data.Map                as Map+import           Test.Hspec++import qualified Control.Monad.Foil      as Foil+import           Control.Monad.Free.Foil+import           Data.ZipMatchK.TH       (deriveZipMatchK)++data LamSig scope term+  = App term term+  | Lam scope+  deriving (Functor, Foldable, Traversable)++deriveBifunctor ''LamSig+deriveBifoldable ''LamSig+deriveBitraversable ''LamSig+deriveZipMatchK ''LamSig++type Term = AST Foil.NameBinder LamSig++data Raw = RVar String | RApp Raw Raw | RLam String Raw++toSig :: Raw -> Either String (LamSig (String, Raw) Raw)+toSig = \case+  RVar x   -> Left x+  RApp f a -> Right (App f a)+  RLam x b -> Right (Lam (x, b))++fromRawPattern+  :: Foil.Distinct n+  => Foil.Scope n+  -> Map String (Foil.Name n)+  -> String+  -> (forall l. Foil.DExt n l => Foil.NameBinder n l -> Map String (Foil.Name l) -> r)+  -> r+fromRawPattern scope names x cont = Foil.withFresh scope $ \binder ->+  cont binder (Map.insert x (Foil.nameOf binder) (fmap Foil.sink names))++-- | The constant @c@, which stands for λy. y.+constants :: Map String (Term Foil.VoidS)+constants = Map.singleton "c" $ Foil.withFresh Foil.emptyScope $ \y ->+  Node (Lam (ScopedAST y (Var (Foil.nameOf y))))++-- | c (λx. x d)+raw :: Raw+raw = RApp (RVar "c") (RLam "x" (RApp (RVar "x") (RVar "d")))++spec :: Spec+spec = do+  describe "unresolvedNames" $+    it "reports a constant as unresolved" $+      unresolvedNames toSig fromRawPattern id Foil.emptyScope Map.empty raw+        `shouldBe` [UnresolvedName "c" [], UnresolvedName "d" ["x"]]++  describe "unresolvedNamesWith" $ do+    it "resolves the constants" $+      unresolvedNamesWith toSig fromRawPattern id Foil.emptyScope Map.empty constants raw+        `shouldBe` [UnresolvedName "d" ["x", "c"]]+    it "reports first what tryConvertToASTWith reports" $+      either Just (const Nothing)+        (tryConvertToASTWith toSig fromRawPattern id Foil.emptyScope Map.empty constants raw)+        `shouldBe` Just (UnresolvedName "d" ["x", "c"])
test/laws/Control/Monad/Free/Foil/Laws.hs view
@@ -613,7 +613,7 @@ -- a free name by its raw identifier. Patterns are compared by the names -- they bind, listed by the given function, and not by their shape. alphaEqNameless-  :: forall binder sig x y. (Bitraversable sig, ZipMatchK sig)+  :: forall binder sig x y. ZipMatchK sig   => PatternNames binder -> AST binder sig x -> AST binder sig y -> Bool alphaEqNameless binders = go 0 IntMap.empty IntMap.empty   where