License | BSD-3-Clause |
---|---|
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
Typechecking and elaboration for the Swarm world DSL. For more information, see:
https://byorgey.wordpress.com/2023/07/13/compiling-to-intrinsically-typed-combinators/
Synopsis
- class Empty e where
- empty :: e
- class Over m where
- (<!>) :: m -> m -> m
- class Applicable t where
- ($$) :: t (a -> b) -> t a -> t b
- type family IsFun a where ...
- type NotFun a = IsFun a ~ 'False
- data Const :: Type -> Type where
- CLit :: (Show a, NotFun a) => a -> Const a
- CCell :: CellVal -> Const CellVal
- CFI :: Const (Integer -> Double)
- CIf :: Const (Bool -> a -> a -> a)
- CNot :: Const (Bool -> Bool)
- CNeg :: (Num a, NotFun a) => Const (a -> a)
- CAbs :: (Num a, NotFun a) => Const (a -> a)
- CAnd :: Const (Bool -> Bool -> Bool)
- COr :: Const (Bool -> Bool -> Bool)
- CAdd :: (Num a, NotFun a) => Const (a -> a -> a)
- CSub :: (Num a, NotFun a) => Const (a -> a -> a)
- CMul :: (Num a, NotFun a) => Const (a -> a -> a)
- CDiv :: (Fractional a, NotFun a) => Const (a -> a -> a)
- CIDiv :: (Integral a, NotFun a) => Const (a -> a -> a)
- CMod :: (Integral a, NotFun a) => Const (a -> a -> a)
- CEq :: (Eq a, NotFun a) => Const (a -> a -> Bool)
- CNeq :: (Eq a, NotFun a) => Const (a -> a -> Bool)
- CLt :: (Ord a, NotFun a) => Const (a -> a -> Bool)
- CLeq :: (Ord a, NotFun a) => Const (a -> a -> Bool)
- CGt :: (Ord a, NotFun a) => Const (a -> a -> Bool)
- CGeq :: (Ord a, NotFun a) => Const (a -> a -> Bool)
- CMask :: (Empty a, NotFun a) => Const (World Bool -> World a -> World a)
- CSeed :: Const Integer
- CCoord :: Axis -> Const (World Integer)
- CHash :: Const (World Integer)
- CPerlin :: Const (Integer -> Integer -> Double -> Double -> World Double)
- CReflect :: Axis -> Const (World a -> World a)
- CRot :: Rot -> Const (World a -> World a)
- COver :: (Over a, NotFun a) => Const (a -> a -> a)
- K :: Const (a -> b -> a)
- S :: Const ((a -> b -> c) -> (a -> b) -> a -> c)
- I :: Const (a -> a)
- B :: Const ((b -> c) -> (a -> b) -> a -> c)
- C :: Const ((a -> b -> c) -> b -> a -> c)
- Φ :: Const ((a -> b -> c) -> (d -> a) -> (d -> b) -> d -> c)
- class HasConst t where
- (.$$) :: (HasConst t, Applicable t) => Const (a -> b) -> t a -> t b
- ($$.) :: (HasConst t, Applicable t) => t (a -> b) -> Const a -> t b
- (.$$.) :: (HasConst t, Applicable t) => Const (a -> b) -> Const a -> t b
- type family Append (xs :: [k]) (ys :: [k]) :: [k] where ...
- data Idx :: [Type] -> Type -> Type where
- idxToNat :: Idx g a -> Int
- weakenVar :: forall h g a. Idx g a -> Idx (Append g h) a
- data TTerm :: [Type] -> Type -> Type where
- weaken :: forall h g a. TTerm g a -> TTerm (Append g h) a
- data CheckErr where
- ApplyErr :: Some (TTerm g) -> Some (TTerm g) -> CheckErr
- NoInstance :: Text -> TTy a -> CheckErr
- Unbound :: Text -> CheckErr
- BadType :: Some (TTerm g) -> TTy b -> CheckErr
- BadDivType :: TTy a -> CheckErr
- UnknownImport :: Text -> CheckErr
- NotAThing :: Text -> CellTag -> CheckErr
- NotAnything :: Text -> CheckErr
- data Base :: Type -> Type where
- data TTy :: Type -> Type where
- pattern TTyBool :: TTy Bool
- pattern TTyInt :: TTy Integer
- pattern TTyFloat :: TTy Double
- pattern TTyCell :: TTy CellVal
- checkEq :: Has (Throw CheckErr) sig m => TTy ty -> ((Eq ty, NotFun ty) => m a) -> m a
- checkOrd :: Has (Throw CheckErr) sig m => TTy ty -> ((Ord ty, NotFun ty) => m a) -> m a
- checkNum :: Has (Throw CheckErr) sig m => TTy ty -> ((Num ty, NotFun ty) => m a) -> m a
- checkIntegral :: Has (Throw CheckErr) sig m => TTy ty -> ((Integral ty, NotFun ty) => m a) -> m a
- checkEmpty :: Has (Throw CheckErr) sig m => TTy ty -> ((Empty ty, NotFun ty) => m a) -> m a
- checkOver :: Has (Throw CheckErr) sig m => TTy ty -> ((Over ty, NotFun ty) => m a) -> m a
- data Some :: (Type -> Type) -> Type where
- mapSome :: (forall α. s α -> t α) -> Some s -> Some t
- type SomeTy = Some (Const ())
- pattern SomeTy :: TTy α -> SomeTy
- type WorldMap = Map Text (Some (TTerm '[]))
- data Ctx :: [Type] -> Type where
- lookup :: Has (Throw CheckErr) sig m => Text -> Ctx g -> m (Some (Idx g))
- check :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> TTy t -> WExp -> m (TTerm g t)
- getBaseType :: Some (TTerm g) -> SomeTy
- apply :: Has (Throw CheckErr) sig m => Some (TTerm g) -> Some (TTerm g) -> m (Some (TTerm g))
- applyTo :: Has (Throw CheckErr) sig m => Some (TTerm g) -> Some (TTerm g) -> m (Some (TTerm g))
- inferOp :: Has (Throw CheckErr) sig m => [SomeTy] -> Op -> m (Some (TTerm g))
- typeArgsFor :: Op -> [Some (TTerm g)] -> [SomeTy]
- applyOp :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> Op -> [WExp] -> m (Some (TTerm g))
- infer :: forall sig m g. (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> WExp -> m (Some (TTerm g))
- resolveCell :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m) => RawCellVal -> m CellVal
- resolveCellItem :: forall sig m. (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m) => (Maybe CellTag, Text) -> m CellVal
- inferLet :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> [(Var, WExp)] -> WExp -> m (Some (TTerm g))
- inferOverlay :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> NonEmpty WExp -> m (Some (TTerm g))
Documentation
class Applicable t where Source #
Instances
Applicable BTerm Source # | |
Applicable CTerm Source # | |
Applicable (OTerm g) Source # | |
Applicable (TTerm g) Source # | |
data Const :: Type -> Type where Source #
Type-indexed constants. These include both language built-ins
(if
, arithmetic, comparison, <>
, etc.) as well as combinators
(S
, I
, C
, K
, B
, Φ
) we will use both for elaboration
and later as a compilation target.
CLit :: (Show a, NotFun a) => a -> Const a | |
CCell :: CellVal -> Const CellVal | |
CFI :: Const (Integer -> Double) | |
CIf :: Const (Bool -> a -> a -> a) | |
CNot :: Const (Bool -> Bool) | |
CNeg :: (Num a, NotFun a) => Const (a -> a) | |
CAbs :: (Num a, NotFun a) => Const (a -> a) | |
CAnd :: Const (Bool -> Bool -> Bool) | |
COr :: Const (Bool -> Bool -> Bool) | |
CAdd :: (Num a, NotFun a) => Const (a -> a -> a) | |
CSub :: (Num a, NotFun a) => Const (a -> a -> a) | |
CMul :: (Num a, NotFun a) => Const (a -> a -> a) | |
CDiv :: (Fractional a, NotFun a) => Const (a -> a -> a) | |
CIDiv :: (Integral a, NotFun a) => Const (a -> a -> a) | |
CMod :: (Integral a, NotFun a) => Const (a -> a -> a) | |
CEq :: (Eq a, NotFun a) => Const (a -> a -> Bool) | |
CNeq :: (Eq a, NotFun a) => Const (a -> a -> Bool) | |
CLt :: (Ord a, NotFun a) => Const (a -> a -> Bool) | |
CLeq :: (Ord a, NotFun a) => Const (a -> a -> Bool) | |
CGt :: (Ord a, NotFun a) => Const (a -> a -> Bool) | |
CGeq :: (Ord a, NotFun a) => Const (a -> a -> Bool) | |
CMask :: (Empty a, NotFun a) => Const (World Bool -> World a -> World a) | |
CSeed :: Const Integer | |
CCoord :: Axis -> Const (World Integer) | |
CHash :: Const (World Integer) | |
CPerlin :: Const (Integer -> Integer -> Double -> Double -> World Double) | |
CReflect :: Axis -> Const (World a -> World a) | |
CRot :: Rot -> Const (World a -> World a) | |
COver :: (Over a, NotFun a) => Const (a -> a -> a) | |
K :: Const (a -> b -> a) | |
S :: Const ((a -> b -> c) -> (a -> b) -> a -> c) | |
I :: Const (a -> a) | |
B :: Const ((b -> c) -> (a -> b) -> a -> c) | |
C :: Const ((a -> b -> c) -> b -> a -> c) | |
Φ :: Const ((a -> b -> c) -> (d -> a) -> (d -> b) -> d -> c) |
data Idx :: [Type] -> Type -> Type where Source #
Type- and context-indexed de Bruijn indices. (v :: Idx g a) means v represents a variable with type a in a type context g.
weakenVar :: forall h g a. Idx g a -> Idx (Append g h) a Source #
A variable valid in one context is also valid in another extended context with additional variables.
data TTerm :: [Type] -> Type -> Type where Source #
Type-indexed terms. Note this is a stripped-down core language, with only variables, lambdas, application, and constants.
TVar :: Idx g a -> TTerm g a | |
TLam :: TTerm (ty1 ': g) ty2 -> TTerm g (ty1 -> ty2) | |
TApp :: TTerm g (a -> b) -> TTerm g a -> TTerm g b | |
TConst :: Const a -> TTerm g a |
Instances
weaken :: forall h g a. TTerm g a -> TTerm (Append g h) a Source #
A term valid in one context is also valid in another extended context with additional variables (which the term does not use).
Errors that can occur during typechecking/elaboration.
ApplyErr :: Some (TTerm g) -> Some (TTerm g) -> CheckErr | |
NoInstance :: Text -> TTy a -> CheckErr | |
Unbound :: Text -> CheckErr | |
BadType :: Some (TTerm g) -> TTy b -> CheckErr | |
BadDivType :: TTy a -> CheckErr | |
UnknownImport :: Text -> CheckErr | |
NotAThing :: Text -> CellTag -> CheckErr | |
NotAnything :: Text -> CheckErr |
data Base :: Type -> Type where Source #
Base types.
Instances
TestEquality Base Source # | Testing base type representations for equality to yield reflected type-level equalities. |
Defined in Swarm.Game.World.Typecheck | |
Show (Base ty) Source # | |
PrettyPrec (Base α) Source # | |
Defined in Swarm.Game.World.Typecheck |
data TTy :: Type -> Type where Source #
Type representations indexed by the corresponding host language type.
TTyBase :: Base t -> TTy t | |
(:->:) :: TTy a -> TTy b -> TTy (a -> b) infixr 0 | |
TTyWorld :: TTy t -> TTy (World t) |
Instances
TestEquality TTy Source # | Testing type representations for equality to yield reflected type-level equalities. |
Defined in Swarm.Game.World.Typecheck | |
Show (TTy ty) Source # | |
PrettyPrec (TTy ty) Source # | |
Defined in Swarm.Game.World.Typecheck |
checkIntegral :: Has (Throw CheckErr) sig m => TTy ty -> ((Integral ty, NotFun ty) => m a) -> m a Source #
checkEmpty :: Has (Throw CheckErr) sig m => TTy ty -> ((Empty ty, NotFun ty) => m a) -> m a Source #
data Some :: (Type -> Type) -> Type where Source #
Wrap up a type-indexed thing to hide the type index, but package
it with a TTy
which we can pattern-match on to recover the type
later.
data Ctx :: [Type] -> Type where Source #
Type contexts, indexed by a type-level list of types of all the variables in the context.
lookup :: Has (Throw CheckErr) sig m => Text -> Ctx g -> m (Some (Idx g)) Source #
Look up a variable name in the context, returning a type-indexed de Bruijn index.
check :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> TTy t -> WExp -> m (TTerm g t) Source #
Check that a term has a given type, and if so, return a
corresponding elaborated and type-indexed term. Note that this
also deals with subtyping: for example, if we check that the term
3
has type World Int
, we will get back a suitably lifted
value (i.e. const 3
).
getBaseType :: Some (TTerm g) -> SomeTy Source #
Get the underlying base type of a term which either has a base type or a World type.
apply :: Has (Throw CheckErr) sig m => Some (TTerm g) -> Some (TTerm g) -> m (Some (TTerm g)) Source #
Apply one term to another term, automatically handling promotion and lifting, via the fact that World is Applicative. That is, (1) if a term of type T is used where a term of type World T is expected, it will automatically be promoted (by an application of const); (2) if a function of type (T1 -> T2 -> ... -> Tn) is applied to any arguments of type (World Ti), the function will be lifted to (World T1 -> World T2 -> ... -> World Tn).
applyTo :: Has (Throw CheckErr) sig m => Some (TTerm g) -> Some (TTerm g) -> m (Some (TTerm g)) Source #
inferOp :: Has (Throw CheckErr) sig m => [SomeTy] -> Op -> m (Some (TTerm g)) Source #
Infer the type of an operator: turn a raw operator into a type-indexed constant. However, some operators are polymorphic, so we also provide a list of type arguments. For example, the type of the negation operator can be either (Int -> Int) or (Float -> Float) so we provide it as an argument.
Currently, all operators take at most one type argument, so (Maybe SomeTy) might seem more appropriate than [SomeTy], but that is just a coincidence; in general one can easily imagine operators that are polymorphic in more than one type variable, and we may wish to add such in the future.
typeArgsFor :: Op -> [Some (TTerm g)] -> [SomeTy] Source #
Given a raw operator and the terms the operator is applied to,
select which types should be supplied as the type arguments to
the operator. For example, for an operator like +
we can just
select the type of its first argument; for an operator like if
,
we must select the type of its second argument, since if : Bool
-> a -> a -> a
. In all cases we must also select the underlying
base type in case the argument has a World
type. For example
if +
is applied to an argument of type World Int
we still
want to give +
the type Int -> Int -> Int
. It can be lifted
to have type World Int -> World Int -> World Int
but that will
be taken care of by application, which will insert the right
combinators to do the lifting.
applyOp :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> Op -> [WExp] -> m (Some (TTerm g)) Source #
Typecheck the application of an operator to some terms, returning a typed, elaborated version of the application.
infer :: forall sig m g. (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m, Has (Reader WorldMap) sig m) => Ctx g -> WExp -> m (Some (TTerm g)) Source #
Infer the type of a term, and elaborate along the way.
resolveCell :: (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m) => RawCellVal -> m CellVal Source #
Try to resolve a RawCellVal
---containing only Text
names for
terrain, entities, and robots---into a real CellVal
with
references to actual terrain, entities, and robots.
resolveCellItem :: forall sig m. (Has (Throw CheckErr) sig m, Has (Reader EntityMap) sig m) => (Maybe CellTag, Text) -> m CellVal Source #
Try to resolve one cell item name into an actual item (terrain, entity, robot, etc.).