swarm-0.7.0.0: src/swarm-lang/Swarm/Language/Types.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Types for the Swarm programming language and related utilities.
module Swarm.Language.Types (
-- * Basic definitions
-- ** Base types and type constructors
BaseTy (..),
baseTyName,
TyCon (..),
Arity (..),
tcArity,
Var,
-- ** Type structure functor
TypeF (..),
-- ** Recursive types
Nat (..),
natToInt,
unfoldRec,
SubstRec (..),
-- * @Type@
Type,
tyVars,
pattern TyConApp,
pattern TyBase,
pattern TyVar,
pattern TyVoid,
pattern TyUnit,
pattern TyInt,
pattern TyText,
pattern TyDir,
pattern TyBool,
pattern TyActor,
pattern TyKey,
pattern TyType,
pattern (:+:),
pattern (:*:),
pattern (:->:),
pattern TyRcd,
pattern TyCmd,
pattern TyDelay,
pattern TyUser,
pattern TyRec,
-- * @UType@
IntVar (..),
UType,
pattern UTyConApp,
pattern UTyBase,
pattern UTyVar,
pattern UTyVar',
pattern UTyVoid,
pattern UTyUnit,
pattern UTyInt,
pattern UTyText,
pattern UTyDir,
pattern UTyBool,
pattern UTyActor,
pattern UTyKey,
pattern UTyType,
pattern UTySum,
pattern UTyProd,
pattern UTyFun,
pattern UTyRcd,
pattern UTyCmd,
pattern UTyDelay,
pattern UTyUser,
pattern UTyRec,
-- ** Utilities
ucata,
mkVarName,
fuvs,
hasAnyUVars,
isTopLevelConstructor,
UnchainableFun (..),
-- * Polytypes
ImplicitQuantification (..),
Poly,
mkPoly,
mkQPoly,
mkTrivPoly,
unPoly,
ptVars,
ptBody,
Polytype,
RawPolytype,
pattern PolyUnit,
UPolytype,
quantify,
absQuantify,
forgetQ,
-- * Contexts
TCtx,
UCtx,
TVCtx,
-- * User type definitions
TydefInfo (..),
tydefType,
tydefArity,
substTydef,
ExpandTydefErr (..),
expandTydef,
expandTydefs,
elimTydef,
TDCtx,
emptyTDCtx,
lookupTD,
lookupTDR,
addBindingTD,
withBindingTD,
resolveUserTy,
-- * WHNF
whnfType,
-- * The 'WithU' class
WithU (..),
) where
import Control.Algebra (Has, run)
import Control.Carrier.Reader (runReader)
import Control.Carrier.Throw.Either (runThrow)
import Control.Effect.Reader (Reader, ask, local)
import Control.Effect.Throw (Throw, throwError)
import Control.Lens (Plated (..), makeLenses, rewriteM, view)
import Control.Monad.Free
import Data.Aeson (FromJSON (..), FromJSON1 (..), ToJSON (..), ToJSON1 (..), genericLiftParseJSON, genericLiftToJSON, genericParseJSON, genericToJSON)
import Data.Data (Data)
import Data.Data.Lens (uniplate)
import Data.Eq.Deriving (deriveEq1)
import Data.Fix
import Data.Foldable (fold)
import Data.Functor.Classes (Eq1)
import Data.Hashable (Hashable (..))
import Data.Hashable.Lifted (Hashable1)
import Data.Kind qualified
import Data.List.NonEmpty ((<|))
import Data.List.NonEmpty qualified as NE
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as M
import Data.MonoidMap (MonoidMap)
import Data.MonoidMap qualified as MM
import Data.MonoidMap.JSON ()
import Data.Ord.Deriving (deriveOrd1)
import Data.Semigroup (Sum (..))
import Data.Set (Set)
import Data.Set qualified as S
import Data.String (IsString (..))
import Data.Text (Text)
import Data.Text qualified as T
import GHC.Generics (Generic, Generic1)
import Prettyprinter (align, braces, brackets, concatWith, flatAlt, hsep, pretty, punctuate, softline, (<+>))
import Swarm.Language.Context (Ctx)
import Swarm.Language.Context qualified as Ctx
import Swarm.Language.TDVar (TDVar, mkTDVar, mkTDVar', tdVarName)
import Swarm.Language.Var (Var)
import Swarm.Pretty (PrettyPrec (..), pparens, pparens', ppr, prettyBinding)
import Swarm.Util (showT, unsnocNE)
import Swarm.Util.JSON (optionsMinimize, optionsUnwrapUnary)
import Text.Show.Deriving (deriveShow1)
import Witch (into)
------------------------------------------------------------
-- Base types
------------------------------------------------------------
-- | Base types.
data BaseTy
= -- | The void type, with no inhabitants.
BVoid
| -- | The unit type, with a single inhabitant.
BUnit
| -- | Signed, arbitrary-size integers.
BInt
| -- | Unicode strings.
BText
| -- | Directions.
BDir
| -- | Booleans.
BBool
| -- | "Actors", i.e. anything that can do stuff. Internally, these
-- are all just "robots", but we give them a more generic
-- in-game name because they could represent other things like
-- aliens, animals, seeds, ...
BActor
| -- | Keys, i.e. things that can be pressed on the keyboard
BKey
| -- | The type of types
BType
deriving (Eq, Ord, Show, Bounded, Enum, Data, Generic, Hashable, FromJSON, ToJSON)
baseTyName :: BaseTy -> Text
baseTyName = into @Text . drop 1 . show
instance PrettyPrec BaseTy where
prettyPrec _ = pretty . drop 1 . show
------------------------------------------------------------
-- Type constructors
------------------------------------------------------------
-- | Type constructors.
data TyCon
= -- | Base types are (nullary) type constructors.
TCBase BaseTy
| -- | Command types.
TCCmd
| -- | Delayed computations.
TCDelay
| -- | Sum types.
TCSum
| -- | Product types.
TCProd
| -- | Function types.
TCFun
| -- | User-defined type constructor.
TCUser TDVar
deriving (Eq, Ord, Show, Data, Generic, Hashable)
instance ToJSON TyCon where
toJSON = genericToJSON optionsMinimize
instance FromJSON TyCon where
parseJSON = genericParseJSON optionsMinimize
instance PrettyPrec TyCon where
prettyPrec _ = \case
TCBase b -> ppr b
TCCmd -> "Cmd"
TCDelay -> "Delay"
TCSum -> "Sum"
TCProd -> "Prod"
TCFun -> "Fun"
TCUser t -> ppr t
-- | The arity of a type, /i.e./ the number of type parameters it
-- expects.
newtype Arity = Arity {getArity :: Int}
deriving (Eq, Ord, Show, Generic, Data, Hashable)
instance ToJSON Arity where
toJSON = genericToJSON optionsUnwrapUnary
instance FromJSON Arity where
parseJSON = genericParseJSON optionsUnwrapUnary
instance PrettyPrec Arity where
prettyPrec _ (Arity a) = pretty a
------------------------------------------------------------
-- Types
------------------------------------------------------------
-- | Peano naturals, for use as de Bruijn indices in recursive types.
data Nat where
NZ :: Nat
NS :: Nat -> Nat
deriving (Eq, Ord, Show, Data, Generic, Hashable, FromJSON, ToJSON)
natToInt :: Nat -> Int
natToInt NZ = 0
natToInt (NS n) = 1 + natToInt n
-- | A "structure functor" encoding the shape of type expressions.
-- Actual types are then represented by taking a fixed point of this
-- functor. We represent types in this way, via a "two-level type",
-- so that we can easily use generic recursion schemes to implement
-- things like substitution.
data TypeF t
= -- | A type constructor applied to some type arguments. For now,
-- all type constructor applications are required to be fully
-- saturated (higher kinds are not supported), so we just
-- directly store a list of all arguments (as opposed to
-- iterating binary application).
TyConF TyCon [t]
| -- | A type variable. The first Var represents the original name,
-- and should be ignored except for use in e.g. error messages.
-- The second Var is the real name of the variable; it may be the
-- same as the first, or it may be different e.g. if it is a
-- freshly generated skolem variable.
TyVarF Var Var
| -- | Record type.
TyRcdF (Map Var t)
| -- | A recursive type variable bound by an enclosing Rec,
-- represented by a de Bruijn index.
TyRecVarF Nat
| -- | Recursive type. The variable is just a suggestion for a name to use
-- when pretty-printing; the actual bound variables are represented
-- via de Bruijn indices.
TyRecF Var t
deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic, Generic1, Data, Hashable)
deriveEq1 ''TypeF
deriveOrd1 ''TypeF
deriveShow1 ''TypeF
instance Hashable1 TypeF -- needs the Eq1 instance
instance ToJSON1 TypeF where
liftToJSON = genericLiftToJSON optionsMinimize
instance FromJSON1 TypeF where
liftParseJSON = genericLiftParseJSON optionsMinimize
-- | @Type@ is now defined as the fixed point of 'TypeF'. It would be
-- annoying to manually apply and match against 'Fix' constructors
-- everywhere, so we provide pattern synonyms that allow us to work
-- with 'Type' as if it were defined in a directly recursive way.
type Type = Fix TypeF
instance Plated Type where
plate = uniplate
newtype IntVar = IntVar Int
deriving (Show, Data, Eq, Ord, Generic, Hashable)
instance PrettyPrec IntVar where
prettyPrec _ = ppr . mkVarName "u"
-- | 'UType's are like 'Type's, but also contain unification
-- variables. 'UType' is defined via 'Free', which is also a kind
-- of fixed point (in fact, @Free TypeF@ is the /free monad/ over
-- 'TypeF').
--
-- Just as with 'Type', we provide a bunch of pattern synonyms for
-- working with 'UType' as if it were defined directly.
type UType = Free TypeF IntVar
-- orphan instance
instance (Eq1 f, Hashable x, Hashable (f (Free f x))) => Hashable (Free f x)
-- | A generic /fold/ for things defined via 'Free' (including, in
-- particular, 'UType').
ucata :: Functor t => (v -> a) -> (t a -> a) -> Free t v -> a
ucata f _ (Pure v) = f v
ucata f g (Free t) = g (fmap (ucata f g) t)
-- | A quick-and-dirty method for turning an 'IntVar' (used internally
-- as a unification variable) into a unique variable name, by
-- appending a number to the given name.
mkVarName :: Text -> IntVar -> Var
mkVarName nm (IntVar v) = T.append nm (showT v)
-- | Get all the free unification variables in a 'UType'.
fuvs :: UType -> Set IntVar
fuvs = ucata S.singleton fold
-- | Check whether a type contains any unification variables at all.
hasAnyUVars :: UType -> Bool
hasAnyUVars = ucata (const True) or
-- | Check whether a type consists of a top-level type constructor
-- immediately applied to unification variables.
isTopLevelConstructor :: UType -> Maybe (TypeF ())
isTopLevelConstructor = \case
Free (TyRcdF m) | all isPure m -> Just (TyRcdF M.empty)
UTyConApp c ts | all isPure ts -> Just (TyConF c [])
_ -> Nothing
isPure :: Free f a -> Bool
isPure (Pure {}) = True
isPure _ = False
-- | For convenience, so we can write /e.g./ @"a"@ instead of @TyVar "a"@.
instance IsString Type where
fromString = TyVar . into @Text
instance IsString UType where
fromString = UTyVar . into @Text
--------------------------------------------------
-- Recursive type utilities
-- | @unfoldRec x t@ unfolds the recursive type @rec x. t@ one step,
-- to @t [(rec x. t) / x]@.
unfoldRec :: SubstRec t => Var -> t -> t
unfoldRec x ty = substRec (TyRecF x ty) ty NZ
-- | Class of type-like things where we can substitute for a bound de
-- Bruijn variable.
class SubstRec t where
-- | @substRec s t n@ substitutes @s@ for the bound de Bruijn variable
-- @n@ everywhere in @t@.
substRec :: TypeF t -> t -> Nat -> t
instance SubstRec (Free TypeF v) where
substRec s = ucata (\i _ -> Pure i) $ \f i -> case f of
TyRecVarF j
| i == j -> Free s
| otherwise -> Free (TyRecVarF j)
TyRecF x g -> Free (TyRecF x (g (NS i)))
_ -> Free (fmap ($ i) f)
instance SubstRec Type where
substRec s = foldFix $ \f i -> case f of
TyRecVarF j
| i == j -> Fix s
| otherwise -> Fix (TyRecVarF j)
TyRecF x g -> Fix (TyRecF x (g (NS i)))
_ -> Fix (fmap ($ i) f)
--------------------------------------------------
-- Pretty-printing machinery for types
-- | Split a function type chain, so that we can pretty print
-- the type parameters aligned on each line when they don't fit.
class UnchainableFun t where
unchainFun :: t -> NE.NonEmpty t
instance UnchainableFun Type where
unchainFun (a :->: ty) = a <| unchainFun ty
unchainFun ty = pure ty
instance UnchainableFun (Free TypeF ty) where
unchainFun (Free (TyConF TCFun [ty1, ty2])) = ty1 <| unchainFun ty2
unchainFun ty = pure ty
instance (UnchainableFun t, PrettyPrec t, SubstRec t) => PrettyPrec (TypeF t) where
prettyPrec p = \case
TyVarF v _ -> ppr v
TyRcdF m -> brackets $ hsep (punctuate "," (map prettyBinding (M.assocs m)))
-- Special cases for type constructors with special syntax.
-- Always use parentheses around sum and product types, see #1625
TyConF TCSum [ty1, ty2] ->
pparens (p > 0) $
prettyPrec 2 ty1 <+> "+" <+> prettyPrec 2 ty2
TyConF TCProd [ty1, ty2] ->
pparens (p > 0) $
prettyPrec 2 ty1 <+> "*" <+> prettyPrec 2 ty2
TyConF TCDelay [ty] -> braces $ ppr ty
TyConF TCFun [ty1, ty2] ->
let (iniF, lastF) = unsnocNE $ ty1 <| unchainFun ty2
funs = (prettyPrec 2 <$> iniF) <> [prettyPrec 1 lastF]
inLine l r = l <+> "->" <+> r
multiLine l r = l <+> "->" <> softline <> r
in pparens' (p > 1) . align $
flatAlt (concatWith multiLine funs) (concatWith inLine funs)
TyRecF x ty ->
pparens (p > 0) $
"rec" <+> ppr x <> "." <+> prettyPrec 0 (substRec (TyVarF x x) ty NZ)
-- This case shouldn't be possible, since TyRecVar should only occur inside a TyRec,
-- and pretty-printing the TyRec (above) will substitute a variable name for
-- any bound TyRecVars before recursing.
TyRecVarF i -> pretty (show (natToInt i))
-- Fallthrough cases for type constructor application. Handles base
-- types, Cmd, user-defined types, or ill-kinded things like 'Int
-- Bool'.
TyConF c [] -> ppr c
TyConF c tys -> pparens (p > 9) $ ppr c <+> hsep (map (prettyPrec 10) tys)
------------------------------------------------------------
-- Generic folding over type representations
------------------------------------------------------------
-- | Type class for various type representations (e.g. 'Type',
-- 'UType') and generic operations we can perform on them. This
-- helps us avoid code duplication in some cases, by writing a
-- single generic function which works for any 'Typical' instance.
class Typical t where
-- | Fold a type into a summary value of some type @a@, given an
-- "empty" value of type @a@ (for use at e.g. Pure nodes in a
-- UType)
foldT :: a -> (TypeF a -> a) -> t -> a
-- | Refold a type into another type. This is a special case of
-- 'foldT' when we want to produce another type, since then we can
-- do something more sensible with 'Pure' nodes in a 'UType'
-- (i.e. preserve them instead of replacing them with a default
-- value).
refoldT :: (TypeF t -> t) -> t -> t
-- | Unroll one level of structure.
unrollT :: t -> Maybe (TypeF t)
-- | Roll up one level of structure.
rollT :: TypeF t -> t
-- | It should be possible to convert 'Type' to any type-ish thing.
fromType :: Type -> t
instance Typical Type where
foldT _ = foldFix
refoldT = foldFix
unrollT (Fix t) = Just t
rollT = Fix
fromType = id
instance Typical UType where
foldT e = ucata (const e)
refoldT = ucata Pure
unrollT (Free t) = Just t
unrollT _ = Nothing
rollT = Free
fromType = toU
-- | Get all the type variables (/not/ unification variables)
-- contained in a 'Type' or 'UType'.
tyVars :: Typical t => t -> Set Var
tyVars = foldT S.empty (\case TyVarF _ x -> S.singleton x; f -> fold f)
------------------------------------------------------------
-- Polytypes
------------------------------------------------------------
data ImplicitQuantification = Unquantified | Quantified
deriving (Eq, Ord, Read, Show)
-- | A @Poly q t@ is a universally quantified @t@. The variables in
-- the list are bound inside the @t@. For example, the type @forall
-- a. a -> a@ would be represented as @Forall ["a"] (TyFun "a"
-- "a")@.
--
-- The type index @q@ is a phantom type index indicating whether the
-- type has been implicitly quantified. Immediately after a
-- polytype is parsed it is 'Unquantified' and unsafe to use. For
-- example, the type @a -> a@ would parse literally as @Forall []
-- (TyFun "a" "a") :: Poly Unquantified Type@, where the type
-- variable @a@ is not in the list of bound variables. Later, after
-- running through 'quantify', it would become a @Poly Quantified
-- Type@, either @Forall ["a"] (TyFun "a" "a")@ if the type variable
-- is implicitly quantified, or unchanged if the type variable @a@
-- was already in scope.
--
-- The @Poly@ constructor intentionally unexported, so that the
-- only way to create a @Poly Quantified@ is through the 'quantify'
-- function.
data Poly (q :: ImplicitQuantification) t = Forall {_ptVars :: [Var], ptBody :: t}
deriving (Show, Eq, Functor, Foldable, Traversable, Data, Generic, FromJSON, ToJSON, Hashable)
-- | Create a raw, unquantified @Poly@ value.
mkPoly :: [Var] -> t -> Poly 'Unquantified t
mkPoly = Forall
-- | Create a polytype while performing implicit quantification.
mkQPoly :: Typical t => t -> Poly 'Quantified t
mkQPoly = absQuantify . Forall []
-- | Create a trivial "polytype" with no bound variables. This is
-- somewhat unsafe --- only use this if you are sure that the polytype
-- you want has no type variables.
mkTrivPoly :: t -> Poly q t
mkTrivPoly = Forall []
-- | Project out the variables and body of a 'Poly' value. It's only
-- possible to project from a 'Poly Quantified' since the list of
-- variables might be meaningless for a type that has not had
-- implicit quantification applied.
unPoly :: Poly 'Quantified t -> ([Var], t)
unPoly (Forall xs t) = (xs, t)
-- | Project out the variables of a 'Poly Quantified' value.
ptVars :: Poly 'Quantified t -> [Var]
ptVars (Forall xs _) = xs
-- | Forget that a polytype has been properly quantified.
forgetQ :: Poly 'Quantified t -> Poly 'Unquantified t
forgetQ (Forall xs t) = Forall xs t
-- | A regular polytype (without unification variables). A @Polytype@
-- (as opposed to a @RawPolytype@) is guaranteed to have implicit
-- quantification properly applied, so that all type variables bound
-- by the forall are explicitly listed.
type Polytype = Poly 'Quantified Type
-- | A raw polytype (without unification variables), which corresponds
-- exactly to the way a polytype was written in source code. In
-- particular there may be type variables which are used in the type
-- but not listed explicitly, which are to be implicitly quantified.
type RawPolytype = Poly 'Unquantified Type
instance PrettyPrec (Poly q Type) where
prettyPrec _ (Forall [] t) = ppr t
prettyPrec _ (Forall xs t) = hsep ("∀" : map ppr xs) <> "." <+> ppr t
-- | A polytype with unification variables.
type UPolytype = Poly 'Quantified UType
instance PrettyPrec (Poly q UType) where
prettyPrec _ (Forall [] t) = ppr t
prettyPrec _ (Forall xs t) = hsep ("∀" : map ppr xs) <> "." <+> ppr t
------------------------------------------------------------
-- WithU
------------------------------------------------------------
-- | In several cases we have two versions of something: a "normal"
-- version, and a @U@ version with unification variables in it
-- (/e.g./ 'Type' vs 'UType', 'Polytype' vs 'UPolytype', 'TCtx' vs
-- 'UCtx'). This class abstracts over the process of converting back
-- and forth between them.
--
-- In particular, @'WithU' t@ represents the fact that the type @t@
-- also has a @U@ counterpart, with a way to convert back and forth.
-- Note, however, that converting back may be "unsafe" in the sense
-- that it requires an extra burden of proof to guarantee that it is
-- used only on inputs that are safe.
class WithU t where
-- | The associated "@U@-version" of the type @t@.
type U t :: Data.Kind.Type
-- | Convert from @t@ to its associated "@U@-version". This
-- direction is always safe (we simply have no unification
-- variables even though the type allows it).
toU :: t -> U t
-- | Convert from the associated "@U@-version" back to @t@.
-- Generally, this direction requires somehow knowing that there
-- are no longer any unification variables in the value being
-- converted.
fromU :: U t -> Maybe t
-- | 'Type' is an instance of 'WithU', with associated type 'UType'.
instance WithU Type where
type U Type = UType
toU = foldFix Free
fromU = ucata (const Nothing) (fmap wrapFix . sequence)
-- | A 'WithU' instance can be lifted through any functor (including,
-- in particular, 'Ctx' and 'Poly').
instance (WithU t, Traversable f) => WithU (f t) where
type U (f t) = f (U t)
toU = fmap toU
fromU = traverse fromU
------------------------------------------------------------
-- Pattern synonyms
------------------------------------------------------------
--------------------------------------------------
-- Type
pattern TyConApp :: TyCon -> [Type] -> Type
pattern TyConApp c tys = Fix (TyConF c tys)
pattern TyBase :: BaseTy -> Type
pattern TyBase b = TyConApp (TCBase b) []
pattern TyVar :: Var -> Type
pattern TyVar x <- Fix (TyVarF _ x)
where
TyVar x = Fix (TyVarF x x)
pattern TyVoid :: Type
pattern TyVoid = TyBase BVoid
pattern TyUnit :: Type
pattern TyUnit = TyBase BUnit
pattern TyInt :: Type
pattern TyInt = TyBase BInt
pattern TyText :: Type
pattern TyText = TyBase BText
pattern TyDir :: Type
pattern TyDir = TyBase BDir
pattern TyBool :: Type
pattern TyBool = TyBase BBool
pattern TyActor :: Type
pattern TyActor = TyBase BActor
pattern TyKey :: Type
pattern TyKey = TyBase BKey
pattern TyType :: Type
pattern TyType = TyBase BType
infixr 5 :+:
pattern (:+:) :: Type -> Type -> Type
pattern ty1 :+: ty2 = TyConApp TCSum [ty1, ty2]
infixr 6 :*:
pattern (:*:) :: Type -> Type -> Type
pattern ty1 :*: ty2 = TyConApp TCProd [ty1, ty2]
infixr 1 :->:
pattern (:->:) :: Type -> Type -> Type
pattern ty1 :->: ty2 = TyConApp TCFun [ty1, ty2]
pattern TyRcd :: Map Var Type -> Type
pattern TyRcd m = Fix (TyRcdF m)
pattern TyCmd :: Type -> Type
pattern TyCmd ty = TyConApp TCCmd [ty]
pattern TyDelay :: Type -> Type
pattern TyDelay ty = TyConApp TCDelay [ty]
pattern TyUser :: TDVar -> [Type] -> Type
pattern TyUser v tys = TyConApp (TCUser v) tys
pattern TyRec :: Var -> Type -> Type
pattern TyRec x ty = Fix (TyRecF x ty)
--------------------------------------------------
-- UType
pattern UTyConApp :: TyCon -> [UType] -> UType
pattern UTyConApp c tys = Free (TyConF c tys)
pattern UTyBase :: BaseTy -> UType
pattern UTyBase b = UTyConApp (TCBase b) []
pattern UTyVar :: Var -> UType
pattern UTyVar x <- Free (TyVarF _ x)
where
UTyVar x = Free (TyVarF x x)
pattern UTyVar' :: Var -> Var -> UType
pattern UTyVar' x y = Free (TyVarF x y)
pattern UTyVoid :: UType
pattern UTyVoid = UTyBase BVoid
pattern UTyUnit :: UType
pattern UTyUnit = UTyBase BUnit
pattern UTyInt :: UType
pattern UTyInt = UTyBase BInt
pattern UTyText :: UType
pattern UTyText = UTyBase BText
pattern UTyDir :: UType
pattern UTyDir = UTyBase BDir
pattern UTyBool :: UType
pattern UTyBool = UTyBase BBool
pattern UTyActor :: UType
pattern UTyActor = UTyBase BActor
pattern UTyKey :: UType
pattern UTyKey = UTyBase BKey
pattern UTyType :: UType
pattern UTyType = UTyBase BType
pattern UTySum :: UType -> UType -> UType
pattern UTySum ty1 ty2 = UTyConApp TCSum [ty1, ty2]
pattern UTyProd :: UType -> UType -> UType
pattern UTyProd ty1 ty2 = UTyConApp TCProd [ty1, ty2]
pattern UTyFun :: UType -> UType -> UType
pattern UTyFun ty1 ty2 = UTyConApp TCFun [ty1, ty2]
pattern UTyRcd :: Map Var UType -> UType
pattern UTyRcd m = Free (TyRcdF m)
pattern UTyCmd :: UType -> UType
pattern UTyCmd ty = UTyConApp TCCmd [ty]
pattern UTyDelay :: UType -> UType
pattern UTyDelay ty = UTyConApp TCDelay [ty]
pattern UTyUser :: TDVar -> [UType] -> UType
pattern UTyUser v tys = UTyConApp (TCUser v) tys
pattern UTyRec :: Var -> UType -> UType
pattern UTyRec x ty = Free (TyRecF x ty)
pattern PolyUnit :: Polytype
pattern PolyUnit = Forall [] (TyCmd TyUnit)
------------------------------------------------------------
-- Contexts
------------------------------------------------------------
-- | A @TCtx@ is a mapping from variables to polytypes. We generally
-- get one of these at the end of the type inference process.
type TCtx = Ctx Var Polytype
-- | A @UCtx@ is a mapping from variables to polytypes with
-- unification variables. We generally have one of these while we
-- are in the midst of the type inference process.
type UCtx = Ctx Var UPolytype
-- | A @TVCtx@ tracks which type variables are in scope, and what
-- skolem variables were assigned to them.
type TVCtx = Ctx Var UType
------------------------------------------------------------
-- Implicit quantification of polytypes
------------------------------------------------------------
-- | Implicitly quantify any otherwise unbound type variables.
quantify ::
(Has (Reader TVCtx) sig m, Typical ty) =>
Poly 'Unquantified ty ->
m (Poly 'Quantified ty)
quantify (Forall xs ty) = do
inScope <- ask @TVCtx
-- Look at all variables which occur in the type but are not
-- explicitly bound by the forall, and and are not bound in the
-- context. Such variables must be implicitly quantified.
let implicit =
(tyVars ty `S.difference` S.fromList xs)
`S.difference` M.keysSet (Ctx.unCtx inScope)
pure $ Forall (xs ++ S.toList implicit) ty
-- | Absolute implicit quantification, i.e. assume there are no type
-- variables in scope.
absQuantify :: Typical t => Poly 'Unquantified t -> Poly 'Quantified t
absQuantify = run . runReader @TVCtx Ctx.empty . quantify
------------------------------------------------------------
-- Type definitions
------------------------------------------------------------
data TydefInfo = TydefInfo
{ _tydefType :: Polytype
, _tydefArity :: Arity
}
deriving (Eq, Show, Generic, Data, FromJSON, ToJSON, Hashable)
makeLenses ''TydefInfo
-- | A @TDCtx@ is a mapping from user-defined type constructor names
-- to their definitions and arities/kinds. It also stores the
-- latest version of each name (for any names with more than one
-- version), so we can tell when a type definition has been
-- shadowed.
data TDCtx = TDCtx
{ getTDCtx :: Ctx TDVar TydefInfo
, getTDVersions :: MonoidMap Text (Sum Int)
}
deriving (Eq, Generic, Show, ToJSON)
-- Need to write manual Hashable instance since MonoidMap
-- does not have instances of its own.
instance Hashable TDCtx where
hashWithSalt s (TDCtx ctx versions) =
s
`hashWithSalt` ctx
`hashWithSalt` (getSum <$> MM.toMap versions)
-- | The empty type definition context.
emptyTDCtx :: TDCtx
emptyTDCtx = TDCtx Ctx.empty MM.empty
-- | Look up a variable in the type definition context.
lookupTD :: TDVar -> TDCtx -> Maybe TydefInfo
lookupTD x = Ctx.lookup x . getTDCtx
-- | Look up a variable in an ambient type definition context.
lookupTDR :: Has (Reader TDCtx) sig m => TDVar -> m (Maybe TydefInfo)
lookupTDR x = lookupTD x <$> ask
-- | Add a binding of a variable name to a type definition, giving it
-- an appropriate version number if it shadows other variables with
-- the same name.
addBindingTD :: Text -> TydefInfo -> TDCtx -> TDCtx
addBindingTD x info (TDCtx tdCtx tdVersions) =
case Ctx.lookup (mkTDVar x) tdCtx of
Nothing -> TDCtx (Ctx.addBinding (mkTDVar x) info tdCtx) tdVersions
Just _ ->
let newVersion = 1 + MM.get x tdVersions
in TDCtx (Ctx.addBinding (mkTDVar' (getSum newVersion) x) info tdCtx) (MM.set x newVersion tdVersions)
-- | Locally extend the ambient type definition context with an
-- additional binding, via 'addBindingTD'.
withBindingTD :: Has (Reader TDCtx) sig m => Text -> TydefInfo -> m a -> m a
withBindingTD x info = local (addBindingTD x info)
-- | Given a parsed variable representing a user-defined type, figure
-- out which version is currently in scope and set the version
-- number of the variable appropriately.
resolveUserTy :: Has (Reader TDCtx) sig m => TDVar -> m TDVar
resolveUserTy v = do
tdCtx <- ask
let x = tdVarName v
let ver = getSum (MM.get x (getTDVersions tdCtx))
pure $ mkTDVar' ver x
newtype ExpandTydefErr = UnexpandedUserType {getUnexpanded :: TDVar}
deriving (Eq, Show)
-- | Expand an application "T ty1 ty2 ... tyn" by looking up the
-- definition of T and substituting ty1 .. tyn for its arguments.
--
-- Note that this has already been kind-checked so we know the
-- number of arguments must match up, and user types must be
-- defined; we don't worry about what happens if the lists have
-- different lengths since in theory that can never happen.
-- However, if T does not exist, we throw an error containing its
-- name.
expandTydef ::
( Has (Reader TDCtx) sig m
, Has (Throw ExpandTydefErr) sig m
, Typical t
) =>
TDVar ->
[t] ->
m t
expandTydef userTyCon tys = do
mtydefInfo <- lookupTDR userTyCon
case mtydefInfo of
Nothing -> throwError (UnexpandedUserType userTyCon)
Just tydefInfo -> pure $ substTydef tydefInfo tys
-- | Expand *all* applications of user-defined type constructors
-- everywhere in a type.
expandTydefs ::
(Has (Reader TDCtx) sig m, Has (Throw ExpandTydefErr) sig m, Typical t, Plated t) =>
t ->
m t
expandTydefs = rewriteM expand
where
-- expand :: t -> m (Maybe t)
expand t = case unrollT t of
Just (TyConF (TCUser u) tys) -> Just <$> expandTydef u tys
_ -> pure Nothing
-- | Given the definition of a type alias, substitute the given
-- arguments into its body and return the resulting type.
substTydef :: forall t. Typical t => TydefInfo -> [t] -> t
substTydef (TydefInfo (Forall as ty) _) tys = refoldT @t substF (fromType ty)
where
argMap = M.fromList $ zip as tys
substF tyF@(TyVarF _ x) = case M.lookup x argMap of
Nothing -> rollT tyF
Just ty' -> ty'
substF tyF = rollT tyF
-- | Replace a type alias with its definition everywhere it occurs
-- inside a type. Typically this is done when reporting the type of
-- a term containing a local tydef: since the tydef is local we
-- can't use it in the reported type.
elimTydef :: forall t. Typical t => TDVar -> TydefInfo -> t -> t
elimTydef x tdInfo = refoldT substF
where
substF = \case
TyConF (TCUser u) tys | u == x -> substTydef tdInfo tys
tyF -> rollT tyF
------------------------------------------------------------
-- Arity
------------------------------------------------------------
-- | The arity, /i.e./ number of type arguments, of each type
-- constructor. Eventually, if we generalize to higher-kinded
-- types, we'll need to upgrade this to return a full-fledged kind
-- instead of just an arity.
tcArity :: TDCtx -> TyCon -> Maybe Arity
tcArity tydefs =
fmap Arity . \case
TCBase _ -> Just 0
TCCmd -> Just 1
TCDelay -> Just 1
TCSum -> Just 2
TCProd -> Just 2
TCFun -> Just 2
TCUser t -> getArity . view tydefArity <$> lookupTD t tydefs
------------------------------------------------------------
-- Reducing types to WHNF
------------------------------------------------------------
-- | Reduce a type to weak head normal form, i.e. keep unfolding type
-- aliases and recursive types just until the top-level constructor
-- of the type is neither @rec@ nor an application of a defined type
-- alias.
whnfType :: TDCtx -> Type -> Type
whnfType tdCtx = run . runReader tdCtx . go
where
go :: Has (Reader TDCtx) sig m => Type -> m Type
go = \case
TyUser u tys -> do
res <- runThrow @ExpandTydefErr (expandTydef u tys)
case res of
Left _ -> pure $ TyUser u tys
Right expTy -> go expTy
TyRec x ty -> go (unfoldRec x ty)
ty -> pure ty