module Type.ExtraChecks (effectTypes, occurs) where
import Prelude hiding (maybe)
import Control.Applicative ((<$>),(<*>))
import Control.Monad.Error
import Control.Monad.State
import qualified Data.Map as Map
import qualified Data.Traversable as Traverse
import qualified Data.UnionFind.IO as UF
import Text.PrettyPrint as P
import qualified AST.Annotation as A
import qualified AST.PrettyPrint as PP
import qualified AST.Type as ST
import qualified AST.Variable as Var
import qualified Type.Hint as Hint
import qualified Type.Type as TT
import qualified Type.State as TS
effectTypes :: TS.Env -> ErrorT [P.Doc] IO (Map.Map String ST.CanonicalType)
effectTypes environment =
do environment' <- liftIO $ Traverse.traverse TT.toSrcType environment
mainCheck environment'
return environment'
mainCheck
:: (Monad m)
=> Map.Map String ST.CanonicalType
-> ErrorT [P.Doc] m ()
mainCheck env =
case Map.lookup "main" env of
Nothing ->
return ()
Just typeOfMain ->
let tipe = ST.deepDealias typeOfMain
in
if tipe `elem` validMainTypes
then return ()
else throwError [ badMainMessage typeOfMain ]
validMainTypes :: [ST.CanonicalType]
validMainTypes =
[ element
, html
, signal element
, signal html
]
where
fromModule :: [String] -> String -> ST.CanonicalType
fromModule home name =
ST.Type (Var.fromModule home name)
html =
fromModule ["VirtualDom"] "Node"
signal tipe =
ST.App (fromModule ["Signal"] "Signal") [ tipe ]
element =
let builtin name =
ST.Type (Var.builtin name)
maybe tipe =
ST.App (fromModule ["Maybe"] "Maybe") [ tipe ]
in
ST.Record
[ ("element", fromModule ["Graphics","Element"] "ElementPrim")
, ("props",
ST.Record
[ ("click" , builtin "_Tuple0")
, ("color" , maybe (fromModule ["Color"] "Color"))
, ("height" , builtin "Int")
, ("hover" , builtin "_Tuple0")
, ("href" , builtin "String")
, ("id" , builtin "Int")
, ("opacity", builtin "Float")
, ("tag" , builtin "String")
, ("width" , builtin "Int")
]
Nothing
)
]
Nothing
badMainMessage :: ST.CanonicalType -> P.Doc
badMainMessage typeOfMain =
P.vcat
[ P.text "Type Error: 'main' must have one of the following types:"
, P.text " "
, P.text " Element, Html, Signal Element, Signal Html"
, P.text " "
, P.text "Instead 'main' has type:\n"
, P.nest 4 (PP.pretty typeOfMain)
, P.text " "
]
occurs :: (String, TT.Variable) -> StateT TS.SolverState IO ()
occurs (name, variable) =
do vars <- liftIO $ infiniteVars [] variable
case vars of
[] ->
return ()
var : _ ->
do desc <- liftIO $ UF.descriptor var
case TT.structure desc of
Nothing ->
TS.addHint (P.text msg)
Just _ ->
do liftIO $ UF.setDescriptor var (desc { TT.structure = Nothing })
var' <- liftIO $ UF.fresh desc
hint <- liftIO $ Hint.create (A.None (P.text name)) (Just msg) var var'
TS.addHint hint
where
msg = "Infinite types are not allowed"
infiniteVars :: [TT.Variable] -> TT.Variable -> IO [TT.Variable]
infiniteVars seen var =
let go = infiniteVars (var:seen) in
if var `elem` seen
then return [var]
else do
desc <- UF.descriptor var
case TT.structure desc of
Nothing -> return []
Just struct ->
case struct of
TT.App1 a b ->
(++) <$> go a <*> go b
TT.Fun1 a b ->
(++) <$> go a <*> go b
TT.Var1 a ->
go a
TT.EmptyRecord1 ->
return []
TT.Record1 fields ext ->
concat <$> mapM go (ext : concat (Map.elems fields))