module Typed.TypeSystem (
typeOf
, appType
, ifType
) where
import Typed.Pretty ( prettyTerm, prettyType )
import Typed.Term ( Term(..) )
import Typed.Type ( Type(..), Context, baptize, dom, codom )
import Maybe ( fromMaybe )
typeOf :: Context -> Term -> Type
typeOf ctx term = case term of
TmUnit -> TpUnit
TmTrue -> TpBool
TmFalse -> TpBool
TmIsZero t
| typeOf ctx t == TpNat -> TpBool
| otherwise -> TpError "only numbers can be tested for zeroness"
TmZero -> TpNat
TmSucc t
| typeOf ctx t == TpNat -> TpNat
| otherwise -> TpError "can only apply succ to numbers"
TmPred t
| typeOf ctx t == TpNat -> TpNat
| otherwise -> TpError "can only apply pred to numbers"
TmVar x -> fromMaybe (baptize x) (lookup x ctx)
TmAbs r@(_,p) t -> p `TpArrow` (typeOf (r:ctx) t)
t1 `TmApp` t2 -> appType p1 p2 where [p1,p2] = map (typeOf ctx) [t1,t2]
TmIf t t1 t2 -> ifType p p1 p2 where [p,p1,p2] = map (typeOf ctx) [t,t1,t2]
TmLoop -> TpError "BOTTOM"
TmWrong _ -> TpWrong
_________________________ -> TpError $ "cannot type term " ++ prettyTerm term
appType :: Type -> Type -> Type
appType p1 p2 = case (dom p1) of
Nothing -> TpError $ prettyType p1 ++ " is not arrow-ish"
Just p1dom
| p1dom /= p2 -> TpError $ "arrow " ++ prettyType p1
++ " should have domain " ++ prettyType p2
| otherwise -> fromMaybe
(TpError $ "cannot infer the codomain of " ++ prettyType p1)
(codom p1)
ifType :: Type -> Type -> Type -> Type
ifType p p1 p2
| p /= TpBool = TpError "predicate is not boolean"
| p1 == p2 = p1
| otherwise = TpError $ "either " ++ prettyType p1 ++ " or " ++ prettyType p2