ivo/src/Ivo/Expression/Base.hs

449 lines
15 KiB
Haskell
Raw Normal View History

2021-03-26 14:55:23 -07:00
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
2021-03-26 14:55:23 -07:00
{-# LANGUAGE UndecidableSuperClasses #-}
2021-03-26 12:31:55 -07:00
module Ivo.Expression.Base
( Text, VoidF, UnitF (..), absurd'
2021-03-26 14:55:23 -07:00
, Expr (..), Ctr (..), Pat, Def, AppArgs, AbsArgs, LetArgs, CtrArgs, AnnX, XExpr
, ExprF (..), PatF (..), DefF (..), AppArgsF, LetArgsF, CtrArgsF, XExprF
2021-03-26 14:55:23 -07:00
, Type (..), TypeF (..), Scheme (..), tapp
, RecursivePhase, projectAppArgs, projectLetArgs, projectCtrArgs, projectXExpr, projectDef
, embedAppArgs, embedLetArgs, embedCtrArgs, embedXExpr, embedDef
, Substitutable, free, freeIn, bound, used, collectVars, rename, rename1
, substitute, substitute1, unsafeSubstitute, unsafeSubstitute1
, runRenamer, freshVar, replaceNames, runSubstituter, maySubstitute
, compose, composeMap
) where
import Control.Monad.Reader (MonadReader, Reader, runReader, asks, local)
import Control.Monad.State (MonadState, StateT, evalStateT, state)
import Control.Monad.Zip (MonadZip, mzipWith)
import Data.Foldable (fold)
2021-03-26 14:55:23 -07:00
import Data.Functor.Foldable (Base, Recursive, Corecursive, project, embed, cata)
import Data.Functor.Foldable.TH (makeBaseFunctor)
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM
import Data.HashSet (HashSet)
import Data.HashSet qualified as HS
2021-03-26 14:55:23 -07:00
import Data.Kind qualified as Kind
import Data.List (foldl1')
import Data.Stream qualified as S
import Data.Text (Text)
import Data.Text qualified as T
data Expr phase
2021-03-26 14:55:23 -07:00
-- | A variable: @x@.
= Var !Text
2021-03-26 14:55:23 -07:00
-- | Function application: @f x_0 ... x_n@.
| App !(Expr phase) !(AppArgs phase)
2021-03-26 14:55:23 -07:00
-- | Lambda abstraction: @λx_0 ... x_n. e@.
| Abs !(AbsArgs phase) !(Expr phase)
2021-03-26 14:55:23 -07:00
-- | Let expression: @let x_0 = v_0 ... ; x_n = v_n in e@.
| Let !(LetArgs phase) !(Expr phase)
2021-03-26 14:55:23 -07:00
-- | Data constructor, e.g. @(x, y)@ or @Left@.
| Ctr !Ctr !(CtrArgs phase)
-- | Case expression to pattern match against a value,
2021-03-26 14:55:23 -07:00
-- e.g. @case { Left x1 -> e1 ; Right x2 -> e2 }@.
| Case ![Pat phase]
2021-03-26 14:55:23 -07:00
-- | Type annotations: @expr : type@.
| Ann !(AnnX phase) !(Expr phase) Type
-- | Additional phase-specific constructors.
| ExprX !(XExpr phase)
type family AppArgs phase
type family AbsArgs phase
type family LetArgs phase
type family CtrArgs phase
2021-03-26 14:55:23 -07:00
type family AnnX phase
type family XExpr phase
2021-03-26 14:55:23 -07:00
class
( c (AppArgs phase)
, c (AbsArgs phase)
, c (LetArgs phase)
, c (CtrArgs phase)
, c (AnnX phase)
, c (XExpr phase)
) => ForallX c phase
instance
( c (AppArgs phase)
, c (AbsArgs phase)
, c (LetArgs phase)
, c (CtrArgs phase)
, c (AnnX phase)
, c (XExpr phase)
) => ForallX c phase
deriving instance ForallX Eq phase => Eq (Expr phase)
deriving instance ForallX Show phase => Show (Expr phase)
-- | Data constructors (used in pattern matching and literals).
data Ctr
-- | `() : ★`
= CUnit
-- | `(x : a, y : b) : a * b`
| CPair
-- | `Left (x : a) : forall b. a + b`
| CLeft
-- | `Right (x : b) : forall a. a + b`
| CRight
-- | `0 : Nat`
| CZero
-- | `1+ (n : Nat) : Nat`
| CSucc
-- | `[] : forall a. List a`
| CNil
-- | `(x : a) :: (xs : List a) : List a`
| CCons
-- | `Char :: Nat -> Char`
| CChar
deriving (Eq, Show)
-- | A single pattern of a case expression, e.g. `(x, y) -> e`.
type Pat phase = PatF (Expr phase)
data PatF r = Pat { patCtr :: !Ctr, patNames :: ![Text], patBody :: !r }
deriving (Eq, Functor, Foldable, Traversable, Show)
-- | A definition, mapping a name to a value.
type Def phase = (Text, Expr phase)
2021-03-26 14:55:23 -07:00
-- | A monomorphic type.
data Type
-- | Type variable.
= TVar Text
-- | Type application.
| TApp Type Type
-- | The function type.
| TAbs
-- | The product type.
| TProd
-- | The sum type.
| TSum
-- | The unit type.
| TUnit
-- | The empty type.
| TVoid
-- | The type of natural numbers.
| TNat
-- | The type of lists.
| TList
-- | The type of characters.
| TChar
deriving (Eq, Show)
tapp :: [Type] -> Type
tapp [] = error "Empty type applications are not permitted"
tapp [t] = t
tapp ts = foldl1' TApp ts
-- | A polymorphic type.
data Scheme
-- | Universally quantified type variables.
= TForall [Text] Type
deriving (Eq, Show)
---
--- Base functor boilerplate for recursion-schemes
---
data ExprF phase r
= VarF !Text
| AppF !r !(AppArgsF phase r)
| AbsF !(AbsArgs phase) r
| LetF !(LetArgsF phase r) r
| CtrF Ctr (CtrArgsF phase r)
| CaseF [PatF r]
2021-03-26 14:55:23 -07:00
| AnnF !(AnnX phase) r Type
| ExprXF !(XExprF phase r)
type instance Base (Expr phase) = ExprF phase
2021-03-26 14:55:23 -07:00
type family AppArgsF phase :: Kind.Type -> Kind.Type
type family LetArgsF phase :: Kind.Type -> Kind.Type
type family CtrArgsF phase :: Kind.Type -> Kind.Type
type family XExprF phase :: Kind.Type -> Kind.Type
class
( c (AppArgsF phase)
, c (LetArgsF phase)
, c (CtrArgsF phase)
, c (XExprF phase)
) => ForallXF c phase
instance
( c (AppArgsF phase)
, c (LetArgsF phase)
, c (CtrArgsF phase)
, c (XExprF phase)
) => ForallXF c phase
data DefF r = Def !Text !r
deriving (Eq, Functor, Foldable, Traversable, Show)
-- | A contractible data type with one extra type parameter.
data UnitF a = Unit
deriving (Eq, Functor, Foldable, Traversable, Show)
-- | An empty type with one extra type parameter.
data VoidF a
deriving (Eq, Functor, Foldable, Traversable, Show)
absurd' :: VoidF a -> b
absurd' x = case x of {}
2021-03-26 14:55:23 -07:00
instance ForallXF Functor phase => Functor (ExprF phase) where
fmap f = \case
VarF n -> VarF n
AppF ef exs -> AppF (f ef) (fmap f exs)
AbsF ns e -> AbsF ns (f e)
LetF ds e -> LetF (fmap f ds) (f e)
CtrF c es -> CtrF c (fmap f es)
CaseF ps -> CaseF (fmap (fmap f) ps)
2021-03-26 14:55:23 -07:00
AnnF x e t -> AnnF x (f e) t
ExprXF q -> ExprXF (fmap f q)
2021-03-26 14:55:23 -07:00
instance ForallXF Foldable phase => Foldable (ExprF phase) where
foldMap f = \case
VarF _ -> mempty
AppF ef exs -> f ef <> foldMap f exs
AbsF _ e -> f e
LetF ds e -> foldMap f ds <> f e
CtrF _ es -> foldMap f es
CaseF ps -> foldMap (foldMap f) ps
2021-03-26 14:55:23 -07:00
AnnF _ e _ -> f e
ExprXF q -> foldMap f q
2021-03-26 14:55:23 -07:00
instance ForallXF Traversable phase => Traversable (ExprF phase) where
traverse f = \case
VarF n -> pure $ VarF n
AppF ef exs -> AppF <$> f ef <*> traverse f exs
AbsF ns e -> AbsF ns <$> f e
LetF ds e -> LetF <$> traverse f ds <*> f e
CtrF c es -> CtrF c <$> traverse f es
CaseF ps -> CaseF <$> traverse (traverse f) ps
2021-03-26 14:55:23 -07:00
AnnF x e t -> (\e' -> AnnF x e' t) <$> f e
ExprXF q -> ExprXF <$> traverse f q
class Functor (ExprF phase) => RecursivePhase phase where
projectAppArgs :: AppArgs phase -> AppArgsF phase (Expr phase)
projectLetArgs :: LetArgs phase -> LetArgsF phase (Expr phase)
projectCtrArgs :: CtrArgs phase -> CtrArgsF phase (Expr phase)
projectXExpr :: XExpr phase -> XExprF phase (Expr phase)
embedAppArgs :: AppArgsF phase (Expr phase) -> AppArgs phase
embedLetArgs :: LetArgsF phase (Expr phase) -> LetArgs phase
embedCtrArgs :: CtrArgsF phase (Expr phase) -> CtrArgs phase
embedXExpr :: XExprF phase (Expr phase) -> XExpr phase
default projectAppArgs :: AppArgs phase ~ AppArgsF phase (Expr phase)
=> AppArgs phase -> AppArgsF phase (Expr phase)
default projectLetArgs :: LetArgs phase ~ LetArgsF phase (Expr phase)
=> LetArgs phase -> LetArgsF phase (Expr phase)
default projectCtrArgs :: CtrArgs phase ~ CtrArgsF phase (Expr phase)
=> CtrArgs phase -> CtrArgsF phase (Expr phase)
default projectXExpr :: XExpr phase ~ XExprF phase (Expr phase)
=> XExpr phase -> XExprF phase (Expr phase)
default embedAppArgs :: AppArgsF phase (Expr phase) ~ AppArgs phase
=> AppArgsF phase (Expr phase) -> AppArgs phase
default embedLetArgs :: LetArgsF phase (Expr phase) ~ LetArgs phase
=> LetArgsF phase (Expr phase) -> LetArgs phase
default embedCtrArgs :: CtrArgsF phase (Expr phase) ~ CtrArgs phase
=> CtrArgsF phase (Expr phase) -> CtrArgs phase
default embedXExpr :: XExprF phase (Expr phase) ~ XExpr phase
=> XExprF phase (Expr phase) -> XExpr phase
projectAppArgs = id
projectLetArgs = id
projectCtrArgs = id
projectXExpr = id
embedAppArgs = id
embedLetArgs = id
embedCtrArgs = id
embedXExpr = id
projectDef :: Def phase -> DefF (Expr phase)
projectDef = uncurry Def
embedDef :: DefF (Expr phase) -> Def phase
embedDef (Def n e) = (n, e)
instance RecursivePhase phase => Recursive (Expr phase) where
project = \case
Var n -> VarF n
App ef exs -> AppF ef (projectAppArgs exs)
Abs ns e -> AbsF ns e
Let ds e -> LetF (projectLetArgs ds) e
Ctr c es -> CtrF c (projectCtrArgs es)
Case ps -> CaseF ps
2021-03-26 14:55:23 -07:00
Ann x e t -> AnnF x e t
ExprX q -> ExprXF (projectXExpr q)
instance RecursivePhase phase => Corecursive (Expr phase) where
embed = \case
VarF n -> Var n
AppF ef exs -> App ef (embedAppArgs exs)
AbsF ns e -> Abs ns e
LetF ds e -> Let (embedLetArgs ds) e
CtrF c es -> Ctr c (embedCtrArgs es)
CaseF ps -> Case ps
2021-03-26 14:55:23 -07:00
AnnF x e t -> Ann x e t
ExprXF q -> ExprX (embedXExpr q)
2021-03-26 14:55:23 -07:00
makeBaseFunctor ''Type
---
--- End base functor boilerplate.
---
class Substitutable e where
-- | Fold over the variables in the expression with a monoid,
-- given what to do with variable usage sites and binding sites respectively.
collectVars :: Monoid m => (Text -> m) -> (Text -> m -> m) -> e -> m
-- | Free variables are variables which occur anywhere in an expression
-- where they are not bound by an abstraction.
free :: e -> HashSet Text
free = collectVars HS.singleton HS.delete
-- | Is the given name a member of the expression's free variables?
freeIn :: Text -> e -> Bool
freeIn name = HS.member name . free
-- | Bound variables are variables which are abstracted over anywhere in an expression.
bound :: e -> HashSet Text
bound = collectVars (const HS.empty) HS.insert
-- | Used variables are variables which appear *anywhere* in an expression, free or bound.
used :: e -> HashSet Text
used = collectVars HS.singleton HS.insert
-- | Given a map between variable names and expressions,
-- replace each free occurrence of a variable with its respective expression.
substitute :: HashMap Text e -> e -> e
substitute substs = unsafeSubstitute substs . rename (foldMap free substs)
substitute1 :: Text -> e -> e -> e
substitute1 n e = substitute (HM.singleton n e)
-- | Rename all bound variables in an expression (both a binding sites and usage sites)
-- with new names where the new names are *not* members of the provided set.
rename :: HashSet Text -> e -> e
rename1 :: Text -> e -> e
rename1 n = rename (HS.singleton n)
-- | A variant of substitution which does *not* avoid variable capture;
-- it only gives the correct result if the bound variables in the body
-- are disjoint from the free variables in the argument.
unsafeSubstitute :: HashMap Text e -> e -> e
unsafeSubstitute1 :: Text -> e -> e -> e
unsafeSubstitute1 n e = unsafeSubstitute (HM.singleton n e)
2021-03-26 14:55:23 -07:00
instance Substitutable Type where
collectVars withVar _ = cata \case
TVarF n -> withVar n
t -> fold t
-- /All/ variables in a monomorphic type are free.
rename _ t = t
-- No renaming step is necessary.
substitute substs = cata \case
TVarF n -> HM.findWithDefault (TVar n) n substs
e -> embed e
unsafeSubstitute = substitute
instance Substitutable Scheme where
collectVars withVar withBinder (TForall names t) =
foldMap withBinder names $ collectVars withVar withBinder t
rename = runRenamer \badNames (TForall names t) ->
uncurry TForall <$> replaceNames badNames names (pure t)
-- I took a shot at implementing this but found it to be quite difficult
-- because merging the foralls is tricky.
-- It's not undoable, but it wasn't worth my further time investment
-- seeing as this function isn't currently used anywhere.
unsafeSubstitute = error "Substitution for schemes not yet implemented"
--
-- These primitives are likely to be useful for implementing `rename`.
-- Ideally, I would like to find a way to move the implementation of `rename` here entirely,
-- but I haven't yet figured out an appropriate abstraction to do so.
--
-- | Run an action which requires a stateful context of used variable names
-- and a local context of variable replacements.
--
-- This is a useful monad for implementing the `rename` function.
runRenamer :: Substitutable e
=> (HashSet Text -> e -> StateT (HashSet Text) (Reader (HashMap Text Text)) a)
-> HashSet Text
-> e
-> a
runRenamer m ctx e = runReader (evalStateT (m ctx e) dirtyNames) HM.empty
where dirtyNames = HS.union ctx (used e)
-- | Create a new variable name within a context of used variable names.
freshVar :: MonadState (HashSet Text) m => Text -> m Text
freshVar baseName =
state \ctx -> let name = newName ctx in (name, HS.insert name ctx)
where
names = S.iterate (`T.snoc` '\'') baseName
newName ctx = S.head $ S.filter (not . (`HS.member` ctx)) names
-- | Replace a collection of old variable names with new variable names
-- and apply those replacements within a context.
replaceNames :: ( MonadReader (HashMap Text Text) m
, MonadState (HashSet Text) m
, MonadZip t, Traversable t
)
=> HashSet Text -> t Text -> m a -> m (t Text, a)
replaceNames badNames names m = do
newNames <- mapM freshVarIfNecessary names
let replacements = HM.filterWithKey (/=) $ fold $ mzipWith HM.singleton names newNames
x <- local (HM.union replacements) m
pure (newNames, x)
where
freshVarIfNecessary name
| name `HS.member` badNames = freshVar name
| otherwise = pure name
---
--- The same as the above section but for `substitute`.
--- This is useful when implementing substitution as a paramorphism.
---
-- | Run an action in a local context of substitutions.
--
-- This monad is useful for implementing, you guessed it, substitution.
runSubstituter :: (e -> Reader (HashMap Text e) a)
-> HashMap Text e
-> e
-> a
runSubstituter m substs e = runReader (m e) substs
-- | Apply only the substitutions which are not bound,
-- and only if there are substitutions left to apply.
maySubstitute :: ( MonadReader (HashMap Text b) m
, Functor t, Foldable t
)
=> t Text -> (a, m a) -> m a
maySubstitute ns (unmodified, substituted) =
local (composeMap HM.delete ns) do
noMoreSubsts <- asks HM.null
if noMoreSubsts
then pure unmodified
else substituted
compose :: Foldable t => t (a -> a) -> (a -> a)
compose = foldr (.) id
composeMap :: (Functor t, Foldable t) => (a -> (b -> b)) -> t a -> (b -> b)
composeMap f = compose . fmap f