module Transform.Canonicalize.Type (tipe) where
import Control.Arrow (second)
import Control.Applicative ((<$>),(<*>))
import Control.Monad.Error
import qualified Data.Map as Map
import Data.Traversable (traverse)
import qualified AST.Type as T
import qualified AST.Variable as Var
import Transform.Canonicalize.Environment
import qualified Transform.Canonicalize.Variable as Canonicalize
tipe :: Environment -> T.RawType -> Canonicalizer String T.CanonicalType
tipe env typ =
let go = tipe env in
case typ of
T.Var x -> return (T.Var x)
T.Type _ -> canonicalizeApp env typ []
T.App t ts -> canonicalizeApp env t ts
T.Lambda a b -> T.Lambda <$> go a <*> go b
T.Aliased name t -> T.Aliased name <$> go t
T.Record fields ext ->
let go' (f,t) = (,) f <$> go t
in T.Record <$> mapM go' fields <*> traverse go ext
canonicalizeApp :: Environment -> T.RawType -> [T.RawType]
-> Canonicalizer String T.CanonicalType
canonicalizeApp env f args =
case f of
T.Type (Var.Raw rawName) ->
do answer <- Canonicalize.tvar env rawName
case answer of
Right alias -> canonicalizeAlias env alias args
Left name -> case args of
[] -> return (T.Type name)
_:_ -> T.App (T.Type name) <$> mapM (tipe env) args
_ -> T.App <$> tipe env f <*> mapM (tipe env) args
canonicalizeAlias :: Environment -> (Var.Canonical, [String], T.CanonicalType)
-> [T.RawType]
-> Canonicalizer String T.CanonicalType
canonicalizeAlias env (name, tvars, dealiasedTipe) tipes =
do when (tipesLen /= tvarsLen) (throwError msg)
tipes' <- mapM (tipe env) tipes
let tipe' = replace (Map.fromList (zip tvars tipes')) dealiasedTipe
return $ T.Aliased name tipe'
where
tipesLen = length tipes
tvarsLen = length tvars
msg :: String
msg = "Type alias '" ++ Var.toString name ++ "' expects " ++ show tvarsLen ++
" type argument" ++ (if tvarsLen == 1 then "" else "s") ++
" but was given " ++ show tipesLen
replace :: Map.Map String T.CanonicalType -> T.CanonicalType -> T.CanonicalType
replace typeTable t =
let go = replace typeTable in
case t of
T.Lambda a b -> T.Lambda (go a) (go b)
T.Var x -> Map.findWithDefault t x typeTable
T.Record fields ext -> T.Record (map (second go) fields) (fmap go ext)
T.Aliased original t' -> T.Aliased original (go t')
T.Type _ -> t
T.App f args -> T.App (go f) (map go args)