implementation module infer import StdEnv import qualified Data.Map as DM from Data.Map import instance Functor (Map k) import qualified Data.Set as DS import Data.Functor import Data.Func import Data.Either import Data.List import Data.Maybe import Control.Applicative import Control.Monad import Control.Monad.Trans import qualified Control.Monad.State as MS import Control.Monad.RWST import ast :: TypeEnv :== 'DM'.Map String Scheme :: Constraint :== (Type, Type) :: Subst :== 'DM'.Map String Type :: Unifier :== (Subst, [Constraint]) :: Infer a :== RWST TypeEnv [Constraint] InferState (Either TypeError) a :: InferState = { count :: Int } :: Solve a :== 'MS'.StateT Unifier (Either TypeError) a :: TypeError = UnboundVariable String nullSubst :: Subst nullSubst = 'DM'.newMap uni :: Type Type -> Infer () uni t1 t2 = tell [(t1, t2)] inEnv :: (String, Scheme) (Infer a) -> Infer a inEnv (x, sc) m = local (\e->'DM'.put x sc $ 'DM'.del x e) m letters :: [String] letters = map toString $ [1..] >>= flip replicateM ['a'..'z'] fresh :: Infer Type fresh = get >>= \s=:{count}->put {s & count=count + 1} >>| pure (VarType $ letters !! count) lookupEnv :: String -> Infer Type lookupEnv x = asks ('DM'.get x) >>= maybe (liftT $ Left $ UnboundVariable x) instantiate class Substitutable a where apply :: Subst a -> a ftv :: a -> 'DS'.Set String instance Substitutable Type where apply s t=:(VarType a) = maybe t id $ 'DM'.get a s apply s (Arrow t1 t2) = Arrow (apply s t1) (apply s t2) apply _ t = t ftv (VarType a) = 'DS'.singleton a ftv (Arrow t1 t2) = 'DS'.union (ftv t1) (ftv t2) ftv t = 'DS'.newSet instance Substitutable Scheme where apply s (Forall as t) = Forall as $ apply (foldr 'DM'.del s as) t ftv (Forall as t) = 'DS'.difference (ftv t) ('DS'.fromList as) instance Substitutable [a] | Substitutable a where apply s ls = map (apply s) ls ftv t = foldr ('DS'.union o ftv) 'DS'.newSet t instance Substitutable TypeEnv where apply s env = fmap (apply s) env ftv env = ftv $ 'DM'.elems env instantiate :: Scheme -> Infer Type instantiate (Forall as t) = mapM (const fresh) as >>= \as`->let s = 'DM'.fromList $ zip2 as as` in pure $ apply s t generalize :: TypeEnv Type -> Scheme generalize env t = Forall ('DS'.toList $ 'DS'.difference (ftv t) (ftv env)) t infer :: Expression -> Infer Type infer (Literal v) = case v of Int _ = pure IntType Bool _ = pure BoolType Char _ = pure CharType infer (Variable s) = lookupEnv s infer (Apply e1 e2) = infer e1 >>= \t1->infer e2 >>= \t2->fresh >>= \tv->uni t1 (Arrow t2 tv) >>| pure tv infer (Lambda s e) = fresh >>= \tv->inEnv (s, Forall [] tv) (infer e) >>= \t-> pure (Arrow tv t) infer (Let x e1 e2) = ask >>= \en->infer e1 >>= \t1->inEnv (x, generalize en t1) (infer e2) infer (Code c) = fresh >>= \v->pure case c of "add" = Arrow v (Arrow v v) "sub" = Arrow v (Arrow v v) "mul" = Arrow v (Arrow v v) "div" = Arrow v (Arrow v v) "and" = Arrow v (Arrow v BoolType) "or" = Arrow v (Arrow v BoolType)