Elm-0.5.0: src/Types/Substitutions.hs
module Types.Substitutions (cSub
,tSub
,cSubNoContext
,hasVarC
,freeVars
,schemeSub
,concretize
,rescheme
,generalize) where
import Control.DeepSeq (NFData (..), deepseq)
import Control.Monad (liftM)
import Data.List (foldl')
import qualified Data.Set as Set
import Guid
import Types
force x = x `deepseq` x
a ++++ b = foldl' (\tl hd -> hd : tl) b a
map' f xs = map (\x -> let v = f x in v `seq` v) xs
instance (NFData a) => NFData (Context c a) where
rnf (Context _ a) = a `deepseq` ()
instance NFData Scheme where
rnf (Forall vs cs t) = vs `deepseq` cs `deepseq` t `deepseq` ()
instance NFData Constraint where
rnf (t1 :=: t2) = t1 `deepseq` t2 `deepseq` ()
rnf (t1 :<: t2) = t1 `deepseq` t2 `deepseq` ()
rnf (t :<<: s) = t `deepseq` s `deepseq` ()
instance NFData Type where
rnf (LambdaT t1 t2) = t1 `deepseq` t2 `deepseq` ()
rnf (ADT _ ts) = foldl' (\acc x -> x `deepseq` acc) () ts
rnf t = t `seq` ()
tSub k v t@(VarT x) = if k == x then v else t
tSub k v (LambdaT t1 t2) = tSub k v t1 `seq` tSub k v t2 `seq` LambdaT (tSub k v t1) (tSub k v t2)
tSub k v (ADT name ts) = map' (tSub k v) ts `seq` ADT name (map' (tSub k v) ts)
tSub k v (Super ts) = Super ts
schemeSubHelp k s c t1 t2 relation = do
(t1',cs1) <- f t1
(t2',cs2) <- f t2
return $ Context c (relation t1' t2') : cs1 ++++ cs2
where f t | hasVar k t = do (v, cs) <- concretize s; return (tSub k v t, cs)
| otherwise = return (t, [])
schemeSub k s (Context ctx (t1 :=: t2)) = t1 `seq` t2 `seq` schemeSubHelp k s ctx t1 t2 (:=:)
schemeSub k s (Context ctx (t1 :<: t2)) = t1 `seq` t2 `seq` schemeSubHelp k s ctx t1 t2 (:<:)
schemeSub k s c@(Context ctx (x :<<: Forall cxs ccs ctipe)) =
if not $ hasVarC k c then return [c]
else do
Forall xs cs tipe <- rescheme s
let constraints = map' (cSub k tipe) $ cs ++ ccs
let c' = x :<<: Forall (cxs ++ xs) constraints (tSub k tipe ctipe)
return [ Context ctx c' ]
hasVar v (VarT x) = v == x
hasVar v (LambdaT t1 t2) = force (hasVar v t1 || hasVar v t2)
hasVar v (ADT _ ts) = force $ any (hasVar v) ts
hasVar v (Super _ ) = False
cSub k v (Context ctx c) = out `deepseq` out
where out = Context ctx (cSubNoContext' k v c)
cSubNoContext k v c = c' `deepseq` c'
where c' = cSubNoContext' k v c
cSubNoContext' k v (t1 :=: t2) = let { t1' = tSub k v $! t1 ; t2' = tSub k v $! t2 } in
t1' `seq` t2' `seq` t1' :=: t2'
cSubNoContext' k v (t :<: super) = let t' = tSub k v t in t' `seq` t' :<: super
cSubNoContext' k v (x :<<: poly@(Forall vs cs tipe)) = (x :<<: poly')
where poly' | k `elem` vs = poly
| otherwise = let tipe' = tSub k v tipe in
tipe' `deepseq` Forall vs (map' (cSub k v) cs) tipe'
concretize (Forall xs cs t) = do
pairs <- zip xs `liftM` mapM (const guid) xs
let tipe = foldl' (\t' (k,v) -> tSub k (VarT v) $! t') t pairs
let f c = foldl' (\c (k,v) -> cSub k (VarT v) $! c) c $! pairs
let cs' = f `seq` map' f cs
tipe `deepseq` cs' `deepseq` return (tipe, cs')
rescheme :: Scheme -> GuidCounter Scheme
rescheme (Forall xs cs t) = do
pairs <- zip xs `liftM` mapM (const guid) xs
let tipe = foldl' (\t' (k,v) -> tSub k (VarT v) $! t') t pairs
let fs = map (\(k,v) -> cSub k (VarT v)) pairs
let cs' = map (\c -> foldl' (\c f -> f $! c) c fs) cs
tipe `deepseq` cs' `deepseq` (return $ Forall (map' snd pairs) cs' tipe)
freeVars t = let fs = freeVars' t in fs `deepseq` fs
freeVars' (VarT v) = [v]
freeVars' (LambdaT t1 t2) = freeVars' t1 ++++ freeVars' t2
freeVars' (ADT _ ts) = concatMap freeVars' ts
freeVars' (Super _ ) = []
cFreeVars c = let fs = cFreeVars' c in fs `deepseq` fs
cFreeVars' (t1 :=: t2) = freeVars' t1 ++++ freeVars' t2
cFreeVars' (t1 :<: t2) = freeVars' t1 ++++ freeVars' t2
cFreeVars' (x :<<: Forall xs cs t) = filter (`notElem` xs) frees
where frees = concatMap (\(Context _ c) -> cFreeVars' c) cs ++++ freeVars' t
hasVarC x (Context _ (t1 :=: t2)) = hasVar x t1 || hasVar x t2
hasVarC x (Context _ (t1 :<: t2)) = hasVar x t1 || hasVar x t2
hasVarC x (Context _ (y :<<: Forall xs cs t)) = x == y || hasVar x t || inCs
where inCs = x `notElem` xs && any (hasVarC x) cs
decontext (Context _ c) = c
generalize :: [X] -> Scheme -> GuidCounter Scheme
generalize exceptions (Forall xs cs t) = rescheme (Forall (xs ++ frees) cs t)
where allFrees = Set.fromList $ freeVars t ++ concatMap (cFreeVars . decontext) cs
frees = Set.toList $ Set.difference allFrees (Set.fromList exceptions)