morloc-0.33.0: library/Morloc/Frontend/Internal.hs
{-|
Module : Morloc.Frontend.Internal
Description : Utilities for type checking
Copyright : (c) Zebulun Arendsee, 2020
License : GPL-3
Maintainer : zbwrnz@gmail.com
Stability : experimental
-}
module Morloc.Frontend.Internal
( (+>)
, (++>)
, Renameable(..)
, Applicable(..)
, Typed(..)
, access1
, access2
, accessWith1
, ann
, anns
, cut
, generalize
, generalizeE
, generalizeTypeSet
, index
, lookupE
, lookupU
, mapU
, mapU'
, newqul
, newvar
, newvarRich
, throwError
, serialConstraint
, incDepth
, decDepth
, getDepth
, langsOf
) where
import Control.Monad.Except (throwError)
import Morloc.Frontend.Namespace
import qualified Control.Monad.State as CMS
import qualified Data.Set as Set
import qualified Morloc.Data.Text as MT
import qualified Morloc.Frontend.PartialOrder as P
class HasManyLanguages a where
langsOf :: Gamma -> a -> [Maybe Lang]
instance HasManyLanguages TypeSet where
langsOf _ (TypeSet Nothing es) = map langOf es
langsOf _ (TypeSet (Just e) es) = langOf e : map langOf es
instance HasManyLanguages Expr where
langsOf g0 e0 = unique $ Nothing : langsOf' g0 e0 where
langsOf' _ (SrcE srcs) = map (Just . srcLang) srcs
langsOf' _ (Signature _ t) = [langOf t]
langsOf' g (Declaration _ e) = langsOf' g e
langsOf' _ UniE = []
langsOf' g (VarE v) = case lookupE v g of
(Just (_, ts)) -> langsOf g ts
Nothing -> []
langsOf' g (AccE e _) = langsOf' g e
langsOf' g (ListE es) = concat . map (langsOf' g) $ es
langsOf' g (TupleE es) = concat . map (langsOf' g) $ es
langsOf' g (LamE _ e) = langsOf' g e
langsOf' g (AppE e1 e2) = langsOf' g e1 ++ langsOf' g e2
langsOf' _ (AnnE _ ts) = map langOf ts
langsOf' _ (NumE _) = []
langsOf' _ (LogE _) = []
langsOf' _ (StrE _) = []
langsOf' g (RecE entries) = concat . map (langsOf' g . snd) $ entries
class Renameable a where
rename :: a -> Stack a
unrename :: a -> a
instance Renameable Expr where
rename = mapU' rename
unrename = mapU unrename
instance Renameable UnresolvedType where
rename t@(VarU _) = return t
rename (ExistU v ts ds) = ExistU <$> pure v <*> (mapM rename ts) <*> (mapM rename ds)
rename (ForallU v t) = do
v' <- rename v
t' <- rename (P.substitute v (VarU v') t)
return $ ForallU v' t'
rename (FunU t1 t2) = FunU <$> rename t1 <*> rename t2
rename (ArrU v ts) = ArrU <$> pure v <*> mapM rename ts
rename (NamU r v ts rs) =
NamU r <$> pure v <*> mapM rename ts <*> mapM (\(x, t) -> (,) <$> pure x <*> rename t) rs
unrename (VarU v) = VarU (unrename v)
unrename (ExistU v ts ds) = ExistU v (map unrename ts) (map unrename ds)
unrename (ForallU v t) = ForallU (unrename v) (unrename t)
unrename (FunU t1 t2) = FunU (unrename t1) (unrename t2)
unrename (ArrU v ts) = ArrU v (map unrename ts)
unrename (NamU r v ts rs) = NamU r v (map unrename ts) [(x, unrename t) | (x, t) <- rs]
instance Renameable TVar where
unrename (TV l t) = TV l . head $ MT.splitOn "." t
rename = newqul
class Applicable a where
apply :: Gamma -> a -> a
-- | Apply a context to a type (See Dunfield Figure 8).
instance Applicable UnresolvedType where
-- [G]a = a
apply _ a@(VarU _) = a
-- [G](A->B) = ([G]A -> [G]B)
apply g (FunU a b) = FunU (apply g a) (apply g b)
-- [G]ForallU a.a = forall a. [G]a
apply g (ForallU x a) = ForallU x (apply g a)
-- [G[a=t]]a = [G[a=t]]t
apply g (ExistU v ts ds) =
case lookupU v g of
-- FIXME: this seems problematic - do I keep the previous parameters or the new ones?
(Just t') -> apply g t' -- reduce an existential; strictly smaller term
Nothing -> ExistU v (map (apply g) ts) (map (apply g) ds)
apply g (ArrU v ts) = ArrU v (map (apply g) ts)
apply g (NamU r v ts rs) = NamU r v (map (apply g) ts) (map (\(n, t) -> (n, apply g t)) rs)
instance Applicable Expr where
apply g e = mapU (apply g) e
instance Applicable EType where
apply g e = e { etype = apply g (etype e) }
class Typed a where
toType :: Maybe Lang -> a -> Maybe UnresolvedType
fromType :: Maybe Lang -> UnresolvedType -> a
instance Typed EType where
toType lang e
| (langOf . etype) e == lang = Just (etype e)
| otherwise = Nothing
fromType _ t =
EType
{ etype = t
, eprop = Set.empty
, econs = Set.empty
}
instance Typed TypeSet where
toType Nothing (TypeSet e _) = e >>= toType Nothing
toType lang (TypeSet _ ts) = case filter (\e -> (langOf . etype) e == lang) ts of
[ ] -> Nothing
[e] -> Just (etype e)
_ -> error "a typeset can contain only one instance of each language"
fromType Nothing t = TypeSet (Just (fromType Nothing t)) []
fromType lang t = TypeSet Nothing [fromType lang t]
serialConstraint :: UnresolvedType -> UnresolvedType -> Stack ()
serialConstraint t1 t2 = do
s <- CMS.get
CMS.put (s {stateSer = (t1, t2):stateSer s})
incDepth :: Stack Int
incDepth = do
s <- CMS.get
let depth = stateDepth s + 1
CMS.put (s {stateDepth = depth})
return depth
decDepth :: Stack Int
decDepth = do
s <- CMS.get
let depth = stateDepth s - 1
CMS.put (s {stateDepth = depth})
return depth
getDepth :: Stack Int
getDepth = CMS.gets stateDepth
mapU :: (UnresolvedType -> UnresolvedType) -> Expr -> Expr
mapU f (LamE v e) = LamE v (mapU f e)
mapU f (ListE es) = ListE (map (mapU f) es)
mapU f (TupleE es) = TupleE (map (mapU f) es)
mapU f (RecE rs) = RecE (zip (map fst rs) (map (mapU f . snd) rs))
mapU f (AppE e1 e2) = AppE (mapU f e1) (mapU f e2)
mapU f (AnnE e ts) = AnnE (mapU f e) (map f ts)
mapU f (Declaration v e) = Declaration v (mapU f e)
mapU f (Signature v e) = Signature v $ e {etype = f (etype e)}
mapU _ e = e
mapU' :: Monad m => (UnresolvedType -> m UnresolvedType) -> Expr -> m Expr
mapU' f (LamE v e) = LamE <$> pure v <*> mapU' f e
mapU' f (ListE es) = ListE <$> mapM (mapU' f) es
mapU' f (RecE rs) = do
es' <- mapM (mapU' f . snd) rs
return $ RecE (zip (map fst rs) es')
mapU' f (TupleE es) = TupleE <$> mapM (mapU' f) es
mapU' f (AppE e1 e2) = AppE <$> mapU' f e1 <*> mapU' f e2
mapU' f (AnnE e ts) = AnnE <$> mapU' f e <*> mapM f ts
mapU' f (Declaration v e) = Declaration <$> pure v <*> mapU' f e
mapU' f (Signature v e) = do
t' <- f (etype e)
return $ Signature v (e {etype = t'})
mapU' _ e = return e
(+>) :: Indexable a => Gamma -> a -> Gamma
(+>) xs x = (index x) : xs
(++>) :: Indexable a => Gamma -> [a] -> Gamma
(++>) g xs = map index (reverse xs) ++ g
-- | remove context up to a marker
cut :: GammaIndex -> Gamma -> Stack Gamma
cut _ [] = throwError EmptyCut
cut i (x:xs)
| i == x = return xs
| otherwise = cut i xs
-- | Look up a type annotated expression
lookupE :: EVar -> Gamma -> Maybe (Expr, TypeSet)
lookupE _ [] = Nothing
lookupE v ((AnnG (Declaration v' e) t):gs)
| v == v' = Just (e, t)
| otherwise = lookupE v gs
lookupE v ((AnnG e@(VarE v') t):gs)
| v == v' = Just (e, t)
| otherwise = lookupE v gs
lookupE v (_:gs) = lookupE v gs
-- | Look up a solved existential type variable
lookupU :: TVar -> Gamma -> Maybe UnresolvedType
lookupU _ [] = Nothing
lookupU v ((SolvedG v' t):gs)
| v == v' = Just t
| otherwise = lookupU v gs
lookupU v (_:gs) = lookupU v gs
access1 :: TVar -> Gamma -> Maybe (Gamma, GammaIndex, Gamma)
access1 v gs =
case findIndex (exists v) gs of
(Just 0) -> Just ([], head gs, tail gs)
(Just i) -> Just (take i gs, gs !! i, drop (i + 1) gs)
_ -> Nothing
where
exists :: TVar -> GammaIndex -> Bool
exists v1 (ExistG v2 _ _) = v1 == v2
exists _ _ = False
accessWith1 :: Monad m =>
(GammaIndex -> Bool) -- ^ method for finding the index
-> (GammaIndex -> m GammaIndex) -- ^ alter GammaIndex
-> (Gamma -> m Gamma) -- ^ default action if the index is not found
-> Gamma -- ^ context that is searched
-> m Gamma
accessWith1 select make def g =
case findIndex select g of
(Just i) ->
case (i, g !! i) of
(0, x) -> make x >>= (\y -> return ([] <> (y : tail g)))
(_, x) -> make x >>= (\y -> return (take i g <> (y : drop (i + 1) g)))
Nothing -> def g
access2 ::
TVar -> TVar
-> Gamma -> Maybe (Gamma, GammaIndex, Gamma, GammaIndex, Gamma)
access2 lv rv gs =
case access1 lv gs of
Just (ls, x, rs) ->
case access1 rv rs of
Just (ls', y, rs') -> Just (ls, x, ls', y, rs')
_ -> Nothing
_ -> Nothing
ann :: Expr -> UnresolvedType -> Expr
ann (AnnE e _) t = AnnE e [t]
ann e@(Declaration _ _) _ = e
ann e@(Signature _ _) _ = e
ann e t = AnnE e [t]
anns :: Expr -> [UnresolvedType] -> Expr
anns (AnnE e _) ts = AnnE e ts
anns e@(Declaration _ _) _ = e
anns e@(Signature _ _) _ = e
anns e ts = AnnE e ts
-- | Deal with existentials.
-- This function is used to resolve remaining existentials when no further
-- inferences about their type can be made. If the existentials have a default
-- type, then that type can be used to replace the existential. Otherwise, the
-- existential can be cast as generic (ForallU).
generalize :: UnresolvedType -> UnresolvedType
generalize = (\t -> generalize' (existentialMap t) t) . setDefaults where
generalize' :: [(TVar, Name)] -> UnresolvedType -> UnresolvedType
generalize' [] t = t
generalize' ((e, r):xs) t = generalize' xs (generalizeOne e r t)
setDefaults :: UnresolvedType -> UnresolvedType
setDefaults (ExistU v ps []) = ExistU v (map setDefaults ps) []
setDefaults (ExistU _ _ (d:_)) = setDefaults d
setDefaults t@(VarU _) = t
setDefaults (ForallU v t) = ForallU v (setDefaults t)
setDefaults (FunU t1 t2) = FunU (setDefaults t1) (setDefaults t2)
setDefaults (ArrU v ts) = ArrU v (map setDefaults ts)
setDefaults (NamU r v ts es)
= NamU r v (map setDefaults ts) (zip (map fst es) (map (setDefaults . snd) es))
variables = [1 ..] >>= flip replicateM ['a' .. 'z']
existentialMap t =
zip (Set.toList (findExistentials t)) (map (Name . MT.pack) variables)
findExistentials :: UnresolvedType -> Set.Set TVar
findExistentials (VarU _) = Set.empty
findExistentials (ExistU v ts ds) =
Set.unions
$ [Set.singleton v]
++ map findExistentials ts
++ map findExistentials ds
findExistentials (ForallU v t) = Set.delete v (findExistentials t)
findExistentials (FunU t1 t2) =
Set.union (findExistentials t1) (findExistentials t2)
findExistentials (ArrU _ ts) = Set.unions (map findExistentials ts)
findExistentials (NamU _ _ ts rs)
= Set.unions (map findExistentials ts ++ map (findExistentials . snd) rs)
generalizeOne :: TVar -> Name -> UnresolvedType -> UnresolvedType
generalizeOne v0@(TV lang0 _) r0 t0 = ForallU (TV lang0 (unName r0)) (f v0 t0)
where
f :: TVar -> UnresolvedType -> UnresolvedType
f v t1@(ExistU v' [] _)
| v == v' = VarU (TV lang0 (unName r0))
| otherwise = t1
f v (ExistU v' ts _)
| v == v' = ArrU (TV lang0 (unName r0)) (map (f v) ts)
| otherwise = ArrU v (map (f v) ts)
f v (FunU t1 t2) = FunU (f v t1) (f v t2)
f v t1@(ForallU x t2)
| v /= x = ForallU x (f v t2)
| otherwise = t1
f v (ArrU v' xs) = ArrU v' (map (f v) xs)
f v (NamU r v' ts xs) = NamU r v' (map (f v) ts) [(k, f v t) | (k, t) <- xs]
f _ t1 = t1
generalizeE :: Expr -> Expr
generalizeE = mapU generalize
generalizeEType :: EType -> EType
generalizeEType e = e {etype = generalize (etype e)}
generalizeTypeSet :: TypeSet -> TypeSet
generalizeTypeSet (TypeSet t ts) =
TypeSet (fmap generalizeEType t) (map generalizeEType ts)
newvar :: Maybe Lang -> Stack UnresolvedType
newvar = newvarRich [] []
newvarRich
:: [UnresolvedType]
-> [UnresolvedType] -- ^ default types
-> Maybe Lang
-> Stack UnresolvedType
newvarRich ps ds lang = do
s <- CMS.get
let v = newvars !! stateVar s
CMS.put $ s {stateVar = stateVar s + 1}
return (ExistU (TV lang v) ps ds)
where
newvars =
zipWith (\x y -> MT.pack (x ++ show y)) (repeat "t") ([0 ..] :: [Integer])
newqul :: TVar -> Stack TVar
newqul (TV l v) = do
s <- CMS.get
let v' = TV l (v <> "." <> (MT.pack . show $ stateQul s)) -- create a new variable such as "a.0"
CMS.put $ s {stateQul = stateQul s + 1}
return v'