243 lines
7.7 KiB
Haskell
243 lines
7.7 KiB
Haskell
{-# LANGUAGE TemplateHaskell #-}
|
|
module Ivo.Types.Base
|
|
( Identity (..)
|
|
, Expr (..), Ctr (..), Pat, ExprF (..), PatF (..), VoidF, UnitF (..), Text
|
|
, substitute, substitute1, rename, rename1, free, bound, used
|
|
, Check, CheckExpr, CheckExprF, CheckX, CheckXF (..)
|
|
, pattern AppFC, pattern CtrC, pattern CtrFC, pattern CallCCC, pattern CallCCFC
|
|
, pattern FixC, pattern FixFC, pattern HoleC, pattern HoleFC
|
|
, Type (..), TypeF (..), Scheme (..), tapp
|
|
, Substitution, Context, Constraint
|
|
, MonoSubstitutable, substituteMono, substituteMono1
|
|
, unparseType, unparseScheme
|
|
) where
|
|
|
|
import Control.Monad (forM)
|
|
import Control.Monad.Reader (asks)
|
|
import Data.Bifunctor (bimap, first)
|
|
import Data.Foldable (fold)
|
|
import Data.Functor.Foldable (embed, cata, para)
|
|
import Data.Functor.Foldable.TH (makeBaseFunctor)
|
|
import Data.Functor.Identity (Identity (..))
|
|
import Data.HashMap.Strict (HashMap)
|
|
import Data.HashMap.Strict qualified as HM
|
|
import Data.List (foldl1')
|
|
import Data.Text qualified as T
|
|
import Data.Traversable (for)
|
|
import Ivo.Expression.Base
|
|
|
|
data Check
|
|
type CheckExpr = Expr Check
|
|
type instance AppArgs Check = CheckExpr
|
|
type instance AbsArgs Check = Text
|
|
type instance LetArgs Check = (Text, CheckExpr)
|
|
type instance CtrArgs Check = UnitF CheckExpr
|
|
type instance XExpr Check = CheckX
|
|
|
|
type CheckX = CheckXF CheckExpr
|
|
|
|
type CheckExprF = ExprF Check
|
|
type instance AppArgsF Check = Identity
|
|
type instance LetArgsF Check = DefF
|
|
type instance CtrArgsF Check = UnitF
|
|
type instance XExprF Check = CheckXF
|
|
|
|
data CheckXF r
|
|
-- | Call-with-current-continuation.
|
|
= CallCCC_
|
|
-- | A fixpoint combinator,
|
|
-- because untyped lambda calculus fixpoint combinators won't typecheck.
|
|
| FixC_
|
|
-- | A hole to ask the type inferencer about the context for debugging purposes.
|
|
| HoleC_
|
|
deriving (Eq, Functor, Foldable, Traversable, Show)
|
|
|
|
pattern CtrC :: Ctr -> CheckExpr
|
|
pattern CtrC c = Ctr c Unit
|
|
|
|
pattern CtrFC :: Ctr -> CheckExprF r
|
|
pattern CtrFC c = CtrF c Unit
|
|
|
|
pattern AppFC :: r -> r -> CheckExprF r
|
|
pattern AppFC ef ex = AppF ef (Identity ex)
|
|
|
|
pattern CallCCC :: CheckExpr
|
|
pattern CallCCC = ExprX CallCCC_
|
|
|
|
pattern CallCCFC :: CheckExprF r
|
|
pattern CallCCFC = ExprXF CallCCC_
|
|
|
|
pattern FixC :: CheckExpr
|
|
pattern FixC = ExprX FixC_
|
|
|
|
pattern FixFC :: CheckExprF r
|
|
pattern FixFC = ExprXF FixC_
|
|
|
|
pattern HoleC :: CheckExpr
|
|
pattern HoleC = ExprX HoleC_
|
|
|
|
pattern HoleFC :: CheckExprF r
|
|
pattern HoleFC = ExprXF HoleC_
|
|
|
|
{-# COMPLETE Var, App, Abs, Let, CtrC, Case, ExprX #-}
|
|
{-# COMPLETE VarF, AppF, AbsF, LetF, CtrFC, CaseF, ExprXF #-}
|
|
{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrF, CaseF, ExprXF #-}
|
|
{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrFC, CaseF, ExprXF #-}
|
|
{-# COMPLETE Var, App, Abs, Let, Ctr, Case, CallCCC, FixC, HoleC #-}
|
|
{-# COMPLETE Var, App, Abs, Let, CtrC, Case, CallCCC, FixC, HoleC #-}
|
|
{-# COMPLETE VarF, AppF, AbsF, LetF, CtrFC, CaseF, CallCCFC, FixFC, HoleFC #-}
|
|
{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrF, CaseF, CallCCFC, FixFC, HoleFC #-}
|
|
{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrFC, CaseF, CallCCFC, FixFC, HoleFC #-}
|
|
|
|
instance RecursivePhase Check where
|
|
projectAppArgs = Identity
|
|
projectLetArgs = projectDef
|
|
|
|
embedAppArgs = runIdentity
|
|
embedLetArgs = embedDef
|
|
|
|
instance Substitutable CheckExpr where
|
|
collectVars withVar withBinder = cata \case
|
|
VarF n -> withVar n
|
|
AbsF n e -> withBinder n e
|
|
LetF (Def n x) e -> x <> withBinder n e
|
|
CaseF pats -> foldMap (\(Pat _ ns e) -> foldr withBinder e ns) pats
|
|
e -> fold e
|
|
|
|
rename = runRenamer $ \badNames -> cata \case
|
|
VarF n -> asks $ Var . HM.findWithDefault n n
|
|
AbsF n e -> uncurry Abs . first runIdentity <$> replaceNames badNames (Identity n) e
|
|
LetF (Def n x) e -> do
|
|
x' <- x
|
|
(Identity n', e') <- replaceNames badNames (Identity n) e
|
|
pure $ Let (n', x') e'
|
|
CaseF ps -> Case <$> forM ps \(Pat ctr ns e) ->
|
|
uncurry (Pat ctr) <$> replaceNames badNames ns e
|
|
e -> embed <$> sequenceA e
|
|
|
|
unsafeSubstitute = runSubstituter $ para \case
|
|
VarF name -> asks $ HM.findWithDefault (Var name) name
|
|
AbsF name e -> Abs name <$> maySubstitute (Identity name) e
|
|
LetF (Def name (_, x)) e -> do
|
|
x' <- x
|
|
e' <- maySubstitute (Identity name) e
|
|
pure $ Let (name, x') e'
|
|
CaseF pats -> Case <$> for pats \(Pat ctr ns e) -> Pat ctr ns <$> maySubstitute ns e
|
|
e -> embed <$> traverse snd e
|
|
|
|
-- | 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)
|
|
|
|
makeBaseFunctor ''Type
|
|
|
|
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
|
|
|
|
-- | A polymorphic type.
|
|
data Scheme
|
|
-- | Universally quantified type variables.
|
|
= TForall [Text] Type
|
|
deriving (Eq, Show)
|
|
|
|
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"
|
|
|
|
type Substitution = HashMap Text Type
|
|
type Context = HashMap Text Scheme
|
|
type Constraint = (Type, Type)
|
|
|
|
class MonoSubstitutable t where
|
|
substituteMono :: Substitution -> t -> t
|
|
|
|
substituteMono1 :: Text -> Type -> t -> t
|
|
substituteMono1 var val= substituteMono (HM.singleton var val)
|
|
|
|
instance MonoSubstitutable Type where
|
|
substituteMono = substitute
|
|
|
|
instance MonoSubstitutable Scheme where
|
|
substituteMono substs (TForall names t) =
|
|
TForall names $ substitute (foldMap HM.delete names substs) t
|
|
|
|
instance MonoSubstitutable Constraint where
|
|
substituteMono substs = bimap (substituteMono substs) (substituteMono substs)
|
|
|
|
instance MonoSubstitutable t => MonoSubstitutable [t] where
|
|
substituteMono = fmap . substituteMono
|
|
|
|
instance MonoSubstitutable t => MonoSubstitutable (HashMap a t) where
|
|
substituteMono = fmap . substituteMono
|
|
|
|
tapp :: [Type] -> Type
|
|
tapp [] = error "Empty type applications are not permitted"
|
|
tapp [t] = t
|
|
tapp ts = foldl1' TApp ts
|
|
|
|
-- HACK
|
|
pattern TApp2 :: Type -> Type -> Type -> Type
|
|
pattern TApp2 tf tx ty = TApp (TApp tf tx) ty
|
|
|
|
-- TODO: Improve these printers.
|
|
unparseType :: Type -> Text
|
|
unparseType (TVar name) = name
|
|
unparseType (TApp2 TAbs a b) = "(" <> unparseType a <> " -> " <> unparseType b <> ")"
|
|
unparseType (TApp2 TProd a b) = "(" <> unparseType a <> " * " <> unparseType b <> ")"
|
|
unparseType (TApp2 TSum a b) = "(" <> unparseType a <> " + " <> unparseType b <> ")"
|
|
unparseType (TApp TList a) = "[" <> unparseType a <> "]"
|
|
unparseType (TApp a b) = "(" <> unparseType a <> " " <> unparseType b <> ")"
|
|
unparseType TAbs = "(->)"
|
|
unparseType TProd = "(*)"
|
|
unparseType TSum = "(+)"
|
|
unparseType TUnit = "★"
|
|
unparseType TVoid = "⊥"
|
|
unparseType TNat = "Nat"
|
|
unparseType TList = "[]"
|
|
unparseType TChar = "Char"
|
|
|
|
unparseScheme :: Scheme -> Text
|
|
unparseScheme (TForall [] t) = unparseType t
|
|
unparseScheme (TForall names t) = "∀" <> T.unwords names <> ". " <> unparseType t
|