Feval-1.0.0.1: FVL/Type.hs
module FVL.Type
( typecheck
) where
import Prelude hiding (lookup)
import qualified Data.Set as Set
import Control.Monad.State
import Control.Applicative
import FVL.Algebra
import FVL.TypeAST
type Equation = (FType, FType)
type Equations = Set.Set Equation
data Updates = Updates Equations Bool
(=>>) :: Updates -> (Equations -> Updates) -> Updates
(=>>) (Updates e True) f = let (Updates e' _) = f e in Updates e' True
(=>>) (Updates e _) f = f e
addEquation' :: Equation -> Equations -> Updates
addEquation' eq e = if Set.member eq e then Updates e False else Updates (Set.insert eq e) True
addEquation :: Equation -> Equations -> Updates
addEquation (x, y) e = addEquation' (x, y) e =>> addEquation' (y, x)
addTransitives :: Equations -> Updates
addTransitives e = Set.fold check_element (Updates e False) e
where check_element eq u = u =>> Set.fold (run_through eq) u
run_through (x, y) (x', y') u = if x' == y
then u =>> addEquation (x, y')
else u
addArrowsAndLists :: Equations -> Updates
addArrowsAndLists e = Set.fold check_element (Updates e False) e
where check_element (FArrow x y, FArrow x' y') u
= u =>> addEquation (x, x') =>> addEquation (y, y')
check_element (FList x, FList y) u
= u =>> addEquation (x, y) =>> addEquation (y, x)
check_element _ u = u
close :: Equations -> Equations
close e = let Updates e' r = addTransitives e =>> addArrowsAndLists in if r
then close e'
else e'
inconsistent :: Equations -> Bool
inconsistent e = Set.fold check False e
where check (FInt, FBool) _ = True
check (FInt, FArrow _ _) _ = True
check (FInt, FList _) _ = True
check (FBool, FArrow _ _) _ = True
check (FBool, FList _) _ = True
check (FArrow _ _, FList _) _ = True
check (FNotClosed, _) _ = True
check _ r = r
choose :: Int -> Equations -> Equation -> FType -> FType
choose _ _ _ FInt = FInt
choose _ _ _ FBool = FBool
choose n e (FVar n', FArrow x y) (FVar n'') = if n' == n
then FArrow (substitute x e) (substitute y e)
else FVar n''
choose n e (FVar n', FList t) r = if n == n'
then FList (substitute t e)
else r
choose n _ (FVar n', y) (FVar n'') = if n /= n' then FVar n'' else case y of
FInt -> FInt
FBool -> FBool
FVar n' -> if n' < n
then if n'' < n' then FVar n'' else FVar n'
else if n'' < n then FVar n'' else FVar n
choose _ _ _ r = r
substitute :: FType -> Equations -> FType
substitute FInt _ = FInt
substitute FBool _ = FBool
substitute (FVar n) e = Set.fold (choose n e) (FVar n) e
substitute (FArrow x y) e = FArrow (substitute x e) (substitute y e)
substitute (FList t) e = FList $ substitute t e
add :: Equation -> Equations -> Equations
add (x, y) e = Set.insert (x, y) (Set.insert (y, x) e)
twoAdd :: Equation -> Equation -> Equations -> Equations -> Equations
twoAdd eq eq' e e' = add eq $ add eq' (Set.union e e')
threeAdd :: Equation -> Equation -> Equation -> Equations -> Equations -> Equations -> Equations
threeAdd eq eq' eq'' e e' e'' = add eq $ Set.union e (twoAdd eq' eq'' e' e'')
type Counter = State Int
doNothing :: Counter Int
doNothing = state (\i -> (i, i))
newHandle :: Counter Int
newHandle = state (\i -> (i, i + 1))
type Hypotheses = [(String, FType)]
lookup :: String -> Hypotheses -> Maybe FType
lookup s [] = Nothing
lookup s ((s', t):xs) = if s' == s then Just t else lookup s xs
type TypeResult = (FType, Equations)
type TypeMAlgebra = MAlgebra Counter (Expr (LazyFix Expr)) TypeResult
alg :: Hypotheses -> TypeMAlgebra
alg _ (CInt _) = (\_ -> (FInt, Set.empty)) <$> doNothing
alg _ (CBool _) = (\_ -> (FBool, Set.empty)) <$> doNothing
alg g (CVar s) = (\_ -> let r = lookup s g in case r of
Nothing -> (FNotClosed, Set.insert (FNotClosed, FNotClosed) Set.empty)
Just t -> (t, Set.empty)) <$> doNothing
alg _ (x `Add` y) = (\(t, e) (t', e') -> (FInt, twoAdd (t, FInt) (t', FInt) e e')) <$> x <*> y
alg _ (x `Sub` y) = (\(t, e) (t', e') -> (FInt, twoAdd (t, FInt) (t', FInt) e e')) <$> x <*> y
alg _ (x `Mul` y) = (\(t, e) (t', e') -> (FInt, twoAdd (t, FInt) (t', FInt) e e')) <$> x <*> y
alg _ (x `Div` y) = (\(t, e) (t', e') -> (FInt, twoAdd (t, FInt) (t', FInt) e e')) <$> x <*> y
alg _ (x `And` y) = (\(t, e) (t', e') -> (FBool, twoAdd (t, FBool) (t', FBool) e e')) <$> x <*> y
alg _ (x `Or` y) = (\(t, e) (t', e') -> (FBool, twoAdd (t, FBool) (t', FBool) e e')) <$> x <*> y
alg _ (x `Equal` y) = (\(t, e) (t', e') -> (FBool, twoAdd (t, FInt) (t', FInt) e e')) <$> x <*> y
alg _ (x `Less` y) = (\(t, e) (t', e') -> (FBool, twoAdd (t, FInt) (t', FInt) e e')) <$> x <*> y
alg _ Empty = (\n -> let h = FVar n in (FList h, Set.empty)) <$> newHandle
alg _ (x `Cons` y) = (\n (t, e) (t', e') -> let h = FVar n in
(FList h, twoAdd (t, h) (t', FList h) e e')) <$> newHandle <*> x <*> y
alg _ (Not x) = (\(t, e) -> (FBool, add (t, FBool) e)) <$> x
alg _ (If p x y) = (\n (t, e) (t', e') (t'', e'') -> let h = FVar n in
(h, threeAdd (t, FBool) (t', t'') (t'', h) e e' e'')) <$> newHandle <*> p <*> x <*> y
alg g (Function x p) = newHandle >>= \n -> let h = FVar n in
typecheck' ((x, h) : g) p >>= \(t, e) -> return (FArrow h t, e)
alg _ (Appl f x) = (\n (t, e) (t', e') -> let h = FVar n in
(h, add (t, FArrow t' h) (Set.union e e'))) <$> newHandle <*> f <*> x
alg g (LetRec f x p r) = newHandle >>= \n -> newHandle >>= \n' ->
let h = FVar n in let h' = FVar n' in
typecheck' ((f, h) : (x, h') : g) p >>= \(t, e) ->
typecheck' ((f, h) : g) r >>= \(t', e') ->
return (t', add (h, FArrow h' t) (Set.union e e'))
alg g (Case p x s s' y) = newHandle >>= \n -> let h = FVar n in p >>= \(t, e) ->
x >>= \(t', e') -> typecheck' ((s, h) : (s', FList h) : g) y >>= \(t'', e'') ->
return (t', twoAdd (t, FList h) (t', t'') e (Set.union e' e''))
typecheck' :: Hypotheses -> LazyFix Expr -> Counter TypeResult
typecheck' g e = lazyMCata (alg g) e
typecheck :: LazyFix Expr -> Maybe FType
typecheck e = let (t, e') = evalState (typecheck' [] e) 0
in let e'' = close e'
in if inconsistent e'' then Nothing else Just (substitute t e'')