elm/compiler/Type/ExtraChecks.hs
2013-08-19 22:39:41 -07:00

61 lines
2.2 KiB
Haskell

-- This module contains checks to be run *after* type inference has
-- completed successfully. At that point we still need to do occurs
-- checks and ensure that `main` has an acceptable type.
module Type.ExtraChecks (extraChecks) where
import Control.Applicative ((<$>),(<*>))
import qualified Data.Map as Map
import qualified Data.UnionFind.IO as UF
import Type.Type ( Variable, structure, Term1(..) )
import Type.State (Env)
import Type.PrettyPrint ( pretty, ParensWhen(Never) )
import Text.PrettyPrint as P
extraChecks :: Env -> IO (Either [P.Doc] Env)
extraChecks env =
case mainCheck env of
Left errs -> return $ Left errs
Right env -> occursCheck env
mainCheck :: Env -> Either [P.Doc] Env
mainCheck env =
case Map.lookup "main" env of
Nothing -> Right env
Just var
| P.render (pretty Never var) `elem` ["Element","Signal Element"] -> Right env
| otherwise ->
Left [ P.vcat [ P.text "Type Error:"
, P.text "'main' must be an Element or a (Signal Element)\n" ] ]
occursCheck :: Env -> IO (Either [P.Doc] Env)
occursCheck env = do
errors <- concat <$> mapM isFinite (Map.toList env)
return $ if null errors then Right env else Left errors
isFinite :: (String, Variable) -> IO [P.Doc]
isFinite (name, var) =
do varIsFinite <- go [] var
return $
case varIsFinite of
True -> []
False -> [ P.vcat [ P.text "Type Error:"
, P.text $ "Cannot construct infinite type for '" ++ name ++ "'\n"
]
]
where
go :: [Variable] -> Variable -> IO Bool
go seen var =
let check = go (var:seen) in
case var `elem` seen of
True -> return False
False -> do
desc <- UF.descriptor var
case structure desc of
Nothing -> return True
Just struct ->
case struct of
App1 a b -> (&&) <$> check a <*> check b
Fun1 a b -> (&&) <$> check a <*> check b
Var1 a -> check a
EmptyRecord1 -> return True
Record1 fields ext -> and <$> mapM check (ext : concat (Map.elems fields))