packages feed

CPL-0.0.5: src/Typing.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Typing
  ( Typing (..)
  , inferType
  , inferType2

  , TI
  , Env
  , runTI
  , getSubst
  , extSubst
  , appSubst
  , unify
  , newFEVar
  ) where

import Variance
import Funct
import qualified FE
import qualified CDT
import Type (GenType (..), Type, FType (..))
import Subst
import AExp
import qualified Exp as E

import Data.List (nub)
import Control.Monad.Error
import Control.Monad.RWS
import qualified Data.Map as Map

----------------------------------------------------------------------------

infix 6 :!
data Typing = AExp :! Type

tm :: Typing -> AExp
tm (e :! _) = e

ty :: Typing -> Type
ty (_ :! t) = t

instance FEs Typing where
  tv (e :! t)      = nub (tv e ++ tv t)
  apply s (e :! t) = apply s e :! apply s t

----------------------------------------------------------------------------

-- Type inference monad
newtype TI a = TI (RWST Env () TIState (Either String) a)
  deriving (Monad, MonadState TIState, MonadReader Env, MonadError String)

type Env = Map.Map E.Id (Either FType Type)
type TIState = (Int, Subst)

runTI :: Env -> TI a -> Either String a
runTI env (TI m) = liftM fst (evalRWST m env initialState)
  where initialState = (0, nullSubst)

getSubst :: TI Subst
getSubst = do
  (_,s) <- get
  return s

extSubst :: Subst -> TI ()
extSubst s2 = do
  (i,s1) <- get
  put (i, s2@@s1)
  return ()

appSubst :: (FEs a) => a -> TI a
appSubst x = do
  s <- getSubst
  return (apply s x)

unify :: FE.FE -> FE.FE -> TI ()
unify a b = do
  s1 <- getSubst
  s2 <- mgu (apply s1 a) (apply s1 b)
  extSubst s2

newFEVar :: TI FE.FE
newFEVar = do
  (i,s) <- get
  put (i+1,s)
  return $ FE.Var i

----------------------------------------------------------------------------

inferType :: E.Exp -> TI Typing
inferType E.Identity = iIdentity
inferType (E.Comp a b) =  do
  a' <- inferType a
  b' <- inferType b
  iComp a' b'
inferType (E.Nat nat) = iNat nat
inferType (E.Fact obj args)  = iFact obj  =<< mapM inferType args
inferType (E.Funct obj args) = iFunct obj =<< mapM inferType args
inferType (E.Var v args) = iVar v =<< mapM inferType args

inferType2 :: [E.Id] -> E.Exp -> TI ([Type], Typing)
inferType2 ps e = do
  ps <- liftM Map.fromList $ forM ps $ \p -> do
    dom <- newFEVar
    cod <- newFEVar
    return (p, dom :-> cod)
  local (Map.union (Map.map Right ps)) $ do
    t <- inferType e
    return (map snd (Map.toList ps), t)

----------------------------------------------------------------------------
-- introduction rules

iIdentity :: TI Typing
iIdentity = do
  v <- newFEVar
  return $ Identity v :! v :-> v

iComp :: Typing -> Typing -> TI Typing
iComp (f :! domf :-> codf) (g :! domg :-> codg) = do
  unify domf codg
  return $ Comp f g :! domg :-> codf

iNat :: CDT.Nat -> TI Typing
iNat nat = do
  annotation <- sequence $ replicate (CDT.natNTypeParams nat) newFEVar
  return $ Nat nat annotation :! apply (zip [0..] annotation) (CDT.natType nat)

iFact :: CDT.CDT -> [Typing] -> TI Typing
iFact obj args = do
  annotation <- sequence $ replicate (CDT.factNTypeParams obj) newFEVar
  let s = zip [0..] annotation
  unifyParams (apply s (CDT.factParams obj)) args
  return $ Fact obj (map tm args) annotation :! apply s (CDT.factDestType obj)

iFunct :: CDT.CDT -> [Typing] -> TI Typing
iFunct obj args = do
  (doml,codl) <- unifyParams (variance obj) args
  return $ Funct obj (map tm args) :! FE.Ap obj doml :-> FE.Ap obj codl
  where
    unifyParams [] [] = return ([],[])
    unifyParams (v:vs) ((_ :! d:->c) : as) = do
      (doms,cods) <- unifyParams vs as
      case v of
        Covariance     -> return (d:doms, c:cods)
        Contravariance -> return (c:doms, d:cods)
        FixedVariance  -> unify d c >> return (d:doms, d:cods)
        FreeVariance   -> do
          x <- newFEVar
          y <- newFEVar
          return (x:doms, y:cods)
    unifyParams _ _ = throwError "wrong number of arguments"

iVar :: E.Id -> [Typing] -> TI Typing
iVar v args = do
  env <- ask
  case Map.lookup v env of
    Just (Right t) ->
      return $ Var v [] [] t :! t
    Just (Left (FType n typs typ)) -> do
      annotation <- sequence (replicate n newFEVar)
      let s = zip [0..] annotation
      unifyParams (apply s typs) args
      let t = apply s typ
      return $ Var v annotation (map tm args) t :! t
    Nothing -> throwError $ "no such variable: " ++ v

unifyParams :: [Type] -> [Typing] -> TI ()
unifyParams [] [] = return ()
unifyParams ((pdom :-> pcod):ps) ((_ :! adom:->acod):as) = do
  unify pdom adom
  unify pcod acod
  unifyParams ps as
  return ()
unifyParams _ _ = throwError "wrong number of arguments"

----------------------------------------------------------------------------