Skip to content

Commit

Permalink
Merge pull request #31 from fizruk/generic-sinkable
Browse files Browse the repository at this point in the history
Add Generic instance for SinkableK
  • Loading branch information
fizruk authored Dec 19, 2024
2 parents 65c6297 + b57f373 commit 7c01aa6
Show file tree
Hide file tree
Showing 9 changed files with 735 additions and 76 deletions.
1 change: 1 addition & 0 deletions haskell/free-foil/free-foil.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ library
Control.Monad.Foil
Control.Monad.Foil.Example
Control.Monad.Foil.Internal
Control.Monad.Foil.Internal.ValidNameBinders
Control.Monad.Foil.Relative
Control.Monad.Foil.TH
Control.Monad.Foil.TH.MkFoilData
Expand Down
2 changes: 2 additions & 0 deletions haskell/free-foil/src/Control/Monad/Foil.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ module Control.Monad.Foil (
unsinkName,
unsinkNamePattern,
-- * Safe (co)sinking and renaming
SinkableK(..),
Sinkable(..),
CoSinkable(..),
HasNameBinders(getNameBinders),
sink,
extendRenaming,
extendNameBinderRenaming,
Expand Down
430 changes: 425 additions & 5 deletions haskell/free-foil/src/Control/Monad/Foil/Internal.hs

Large diffs are not rendered by default.

178 changes: 178 additions & 0 deletions haskell/free-foil/src/Control/Monad/Foil/Internal/ValidNameBinders.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
module Control.Monad.Foil.Internal.ValidNameBinders where

import Data.Kind (Type, Constraint)
import GHC.TypeError
import GHC.TypeLits
import Generics.Kind
import qualified GHC.Generics as GHC

type SubstInRepK :: TyVar d k -> Atom d k -> (LoT d -> Type) -> LoT d -> Type
type family SubstInRepK i atom f where
SubstInRepK i atom V1 = V1
SubstInRepK i atom U1 = U1
SubstInRepK i atom (M1 info c f) = M1 info c (SubstInRepK i atom f)
SubstInRepK i atom (Field field) = Field (SubstInAtom i atom field)
SubstInRepK i atom f =
TypeError
('Text "cannot substitute variable"
:$$: 'Text " " :<>: 'ShowType (Var i)
:$$: 'Text "with atom"
:$$: 'Text " " :<>: 'ShowType atom
:$$: 'Text "in"
:$$: 'Text " " :<>: 'ShowType f)

type SubstInAtom :: TyVar d k -> Atom d k -> Atom d k1 -> Atom d k1
type family SubstInAtom i atom f where
SubstInAtom i atom (Var i) = atom
SubstInAtom i atom (Var j) = Var j
SubstInAtom i atom (Kon a) = Kon a
SubstInAtom i atom (f :@: x) = SubstInAtom i atom f :@: SubstInAtom i atom x
-- SubstInAtom i atom atom' = atom'
SubstInAtom i atom atom' =
TypeError
('Text "cannot substitute variable"
:$$: 'Text " " :<>: 'ShowType (Var i)
:$$: 'Text "with atom"
:$$: 'Text " " :<>: 'ShowType atom
:$$: 'Text "in an atom"
:$$: 'Text " " :<>: 'ShowType atom')

type ShowKindedScope oo n ll = ShowScope oo n ll :<>: 'Text " : S"

type ShowScope :: Atom d s -> Atom d s -> Atom d s -> ErrorMessage
type family ShowScope oo n ll where
ShowScope oo ll ll = 'Text "innerScope"
ShowScope oo oo ll = 'Text "outerScope"
ShowScope oo n ll = ShowScopeN 1 n

type ShowScopeN :: Natural -> Atom d s -> ErrorMessage
type family ShowScopeN i n where
ShowScopeN i (Var VZ) = 'Text "scope" :<>: 'ShowType i
ShowScopeN i (Var (VS x)) = ShowScopeN (i + 1) (Var x)

type ShowSaturatedPatternType pattern oo n l ll =
'ShowType pattern :<>: 'Text " " :<>: ShowScope oo n ll :<>: 'Text " " :<>: ShowScope oo l ll

type GInnerScopeOfAtom :: ErrorMessage -> Nat -> Nat -> (s -> s -> Type) -> Atom d Type -> Atom d s -> Atom d s -> Atom d s -> Atom d s
type family GInnerScopeOfAtom msg icon ifield pattern atom oo n ll where
GInnerScopeOfAtom msg icon ifield pattern (Kon f :@: n :@: l) oo n ll = l
GInnerScopeOfAtom msg icon ifield pattern (Kon f :@: o :@: i) oo n ll =
TypeError
('Text "Unexpected Foil scope extension in the binder/pattern"
:$$: 'Text " " :<>: ShowSaturatedPatternType f oo o i ll
:$$: 'Text "the binder/pattern tries to extend scope"
:$$: 'Text " " :<>: ShowKindedScope oo o ll
:$$: 'Text "to scope"
:$$: 'Text " " :<>: ShowKindedScope oo i ll
:$$: 'Text "but it is expected to extend the current (outer) scope"
:$$: 'Text " " :<>: ShowKindedScope oo n ll
:$$: ShowLocalizeError msg icon ifield pattern oo ll
)
GInnerScopeOfAtom msg icon ifield pattern atom oo n ll = n

type SameInnerScope :: ErrorMessage -> Nat -> (s -> s -> Type) -> Atom k s -> Atom k s -> Atom k s
type family SameInnerScope msg icon pattern n l where
SameInnerScope msg icon pattern l l = l
SameInnerScope msg icon pattern n l =
TypeError
('Text "Unexpected extended (inner) Foil scope in the data type"
:$$: 'Text " " :<>: ShowSaturatedPatternType pattern n n l l
:$$: 'Text "expecting extended (inner) scope to be"
:$$: 'Text " " :<>: ShowKindedScope n l l
:$$: 'Text "but the inferred extended (inner) scope is"
:$$: 'Text " " :<>: ShowKindedScope n n l
:$$: ShowLocalizeError msg icon 0 pattern n l
)

type GValidNameBinders :: (s -> s -> Type) -> (LoT (s -> s -> Type) -> Type) -> Constraint
type family GValidNameBinders pattern f :: Constraint where
GValidNameBinders pattern (f :: LoT (s -> s -> Type) -> Type) =
(GInnerScopeOfRepK ('Text "") 0 0 pattern f Var0 Var0 Var1)
~ (Var1 :: Atom (s -> s -> Type) s)

type AtomSucc :: Atom d k1 -> Atom (k -> d) k1
type family AtomSucc atom where
AtomSucc (Var i) = Var (VS i)

type AtomUnSucc :: ErrorMessage -> Nat -> (s -> s -> Type) -> Atom d s -> Atom d s -> Atom (k -> d) k1 -> Atom d k1
type family AtomUnSucc msg icon pattern oo ll atom where
AtomUnSucc msg icon pattern oo ll (Var (VS i)) = Var i
AtomUnSucc msg icon pattern oo ll (Var VZ) = TypeError
('Text "Intermediate scope escapes existential quantification for data type"
:$$: ShowLocalizeError msg icon 0 pattern oo ll
)

type family First x y where
First (Var x) (Var y) = Var x

type family AndShowFieldNumber ifield msg where
AndShowFieldNumber 0 msg = msg
AndShowFieldNumber n msg =
'Text "when checking field number " :<>: 'ShowType n
:$$: msg

type family AndShowConNumber icon msg where
AndShowConNumber 0 msg = msg
AndShowConNumber n msg =
'Text "when checking constructor number " :<>: 'ShowType n
:$$: msg

type AndShowDataType pattern n l msg =
'Text "when tracking Foil scopes for the data type"
:$$: 'Text " " :<>: ShowSaturatedPatternType pattern n n l l
:$$: msg

type ShowLocalizeError msg icon ifield pattern o l =
AndShowFieldNumber ifield
(AndShowConNumber icon
(AndShowDataType pattern o l
msg))

type family CountCons f where
CountCons (f :+: g) = CountCons f + CountCons g
CountCons (M1 GHC.C c f) = 1

type family CountFields f where
CountFields (f :*: g) = CountFields f + CountFields g
CountFields (M1 GHC.S c f) = 1

type GInnerScopeOfRepK :: ErrorMessage -> Nat -> Nat -> (s -> s -> Type) -> (LoT k -> Type) -> Atom k s -> Atom k s -> Atom k s -> Atom k s
type family GInnerScopeOfRepK msg icon ifield pattern f o n l where
GInnerScopeOfRepK msg icon ifield pattern V1 o n l = l
GInnerScopeOfRepK msg icon ifield pattern U1 o n l = n
GInnerScopeOfRepK msg icon ifield pattern (M1 GHC.C c f) o n l =
GInnerScopeOfRepK msg icon 1 pattern f o n l
GInnerScopeOfRepK msg icon ifield pattern (M1 GHC.D c f) o n l =
GInnerScopeOfRepK msg 1 ifield pattern f o n l
GInnerScopeOfRepK msg icon ifield pattern (M1 i c f) o n l =
GInnerScopeOfRepK msg icon ifield pattern f o n l
GInnerScopeOfRepK msg icon ifield pattern (f :+: g) o n l = First
(SameInnerScope msg icon pattern (GInnerScopeOfRepK msg icon ifield pattern f o n l) l)
(SameInnerScope msg icon pattern (GInnerScopeOfRepK msg (icon + CountCons f) ifield pattern g o n l) l)
GInnerScopeOfRepK msg icon ifield pattern (f :*: g) o n l =
GInnerScopeOfRepK msg icon (ifield + CountFields f) pattern g o
(GInnerScopeOfRepK msg icon ifield pattern f o n l) l
GInnerScopeOfRepK msg icon ifield pattern (Var i :~~: Var j :=>: f) o n l =
GInnerScopeOfRepK msg icon ifield pattern (SubstInRepK i (Var j) f)
(SubstInAtom i (Var j) o) (SubstInAtom i (Var j) n) (SubstInAtom i (Var j) l)
GInnerScopeOfRepK msg icon ifield pattern (Exists k f) o n l =
AtomUnSucc msg icon pattern o l
(GInnerScopeOfRepK msg icon ifield pattern f (AtomSucc o) (AtomSucc n) (AtomSucc l))
GInnerScopeOfRepK msg icon ifield pattern (Field atom) o n l = GInnerScopeOfAtom msg icon ifield pattern atom o n l
GInnerScopeOfRepK msg icon ifield pattern f o n l =
TypeError
('Text "Unsupported structure in a binder/pattern"
:$$: 'Text " " :<>: 'ShowType f
:$$: ShowLocalizeError msg icon 0 pattern n l)

type PutBackLoT :: TyVar d s -> s -> LoT k -> LoT k
type family PutBackLoT i c bs where
PutBackLoT VZ c (b :&&: bs) = c :&&: bs
PutBackLoT (VS x) c (b :&&: bs) = b :&&: PutBackLoT x c bs
50 changes: 33 additions & 17 deletions haskell/free-foil/src/Control/Monad/Free/Foil.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
Expand Down Expand Up @@ -26,6 +28,8 @@ 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.Coerce (coerce)
import Data.Map (Map)
import qualified Data.Map as Map
Expand Down Expand Up @@ -53,14 +57,25 @@ deriving instance Generic (AST binder sig n)
deriving instance (forall x y. NFData (binder x y), forall scope term. (NFData scope, NFData term) => NFData (sig scope term))
=> NFData (AST binder sig n)

instance (Bifunctor sig, Foil.CoSinkable binder) => Foil.Sinkable (AST binder sig) where
sinkabilityProof rename = \case
Var name -> Var (rename name)
Node node -> Node (bimap f (Foil.sinkabilityProof rename) node)
where
f (ScopedAST binder body) =
Foil.extendRenaming rename binder $ \rename' binder' ->
ScopedAST binder' (Foil.sinkabilityProof rename' body)
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
Expand All @@ -69,7 +84,7 @@ instance Foil.InjectName (AST binder sig) where

-- | Substitution for free (scoped monads).
substitute
:: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder)
:: (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
Expand All @@ -94,7 +109,7 @@ substitute scope subst = \case
--
-- In general, 'substitute' is more efficient since it does not always refresh binders.
substituteRefreshed
:: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder)
:: (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
Expand All @@ -111,7 +126,8 @@ substituteRefreshed scope subst = \case
in ScopedAST binder' body'

-- | @'AST' sig@ is a monad relative to 'Foil.Name'.
instance (Bifunctor sig, Foil.CoSinkable binder) => Foil.RelMonad Foil.Name (AST binder sig) where
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
Expand All @@ -127,7 +143,7 @@ instance (Bifunctor sig, Foil.CoSinkable binder) => Foil.RelMonad Foil.Name (AST

-- | Substitution for a single generalized pattern.
substitutePattern
:: (Bifunctor sig, Foil.Distinct o, Foil.CoSinkable binder', Foil.CoSinkable binder)
:: (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@.
Expand All @@ -143,7 +159,7 @@ substitutePattern scope env binders args body =

-- | Refresh (force) all binders in a term, minimizing the used indices.
refreshAST
:: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder)
:: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder, Foil.SinkableK binder)
=> Foil.Scope n
-> AST binder sig n
-> AST binder sig n
Expand All @@ -152,7 +168,7 @@ refreshAST scope = \case
Node t -> Node (bimap (refreshScopedAST scope) (refreshAST scope) t)

-- | Similar to `refreshAST`, but for scoped terms.
refreshScopedAST :: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder)
refreshScopedAST :: (Bifunctor sig, Foil.Distinct n, Foil.CoSinkable binder, Foil.SinkableK binder)
=> Foil.Scope n
-> ScopedAST binder sig n
-> ScopedAST binder sig n
Expand All @@ -168,7 +184,7 @@ refreshScopedAST scope (ScopedAST binder body) =
-- Compared to 'alphaEquiv', this function may perform some unnecessary
-- changes of bound variables when the binders are the same on both sides.
alphaEquivRefreshed
:: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder)
:: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)
=> Foil.Scope n
-> AST binder sig n
-> AST binder sig n
Expand All @@ -181,7 +197,7 @@ alphaEquivRefreshed scope t1 t2 = refreshAST scope t1 `unsafeEqAST` refreshAST s
-- Compared to 'alphaEquivRefreshed', this function might skip unnecessary
-- changes of bound variables when both binders in two matching scoped terms coincide.
alphaEquiv
:: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder)
:: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)
=> Foil.Scope n
-> AST binder sig n
-> AST binder sig n
Expand All @@ -195,7 +211,7 @@ alphaEquiv _ _ _ = False

-- | Same as 'alphaEquiv' but for scoped terms.
alphaEquivScoped
:: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder)
:: (Bitraversable sig, ZipMatchK sig, Foil.Distinct n, Foil.UnifiablePattern binder, Foil.SinkableK binder)
=> Foil.Scope n
-> ScopedAST binder sig n
-> ScopedAST binder sig n
Expand Down
Loading

0 comments on commit 7c01aa6

Please sign in to comment.