morloc-0.33.0: library/Morloc/Frontend/PartialOrder.hs
{-|
Module : Morloc.Frontend.PartialOrder
Description : Partial order implementation for types
Copyright : (c) Zebulun Arendsee, 2020
License : GPL-3
Maintainer : zbwrnz@gmail.com
Stability : experimental
-}
module Morloc.Frontend.PartialOrder (
substitute
, free
, isSubtypeOf
, equivalent
, mostGeneral
, mostSpecific
, mostSpecificSubtypes
, (<=)
) where
import Morloc.Frontend.Namespace
import qualified Morloc.Data.Text as MT
import qualified Data.Set as Set
import qualified Data.PartialOrd as P
-- | substitute all appearances of a given variable with a given new type
substitute :: TVar -> UnresolvedType -> UnresolvedType -> UnresolvedType
substitute v (ForallU q r) t =
if Set.member (VarU q) (free t)
then
let q' = getNewVariable r t -- get unused variable name from [a, ..., z, aa, ...]
r' = substitute q (VarU q') r -- substitute the new variable into the unqualified type
in ForallU q' (substitute v r' t)
else
ForallU q (substitute v r t)
substitute v r t = sub t
where
sub :: UnresolvedType -> UnresolvedType
sub t'@(VarU v')
| v == v' = r
| otherwise = t'
sub (FunU t1 t2) = FunU (sub t1) (sub t2)
sub t'@(ForallU x t'')
| v /= x = ForallU x (sub t'')
| otherwise = t' -- allows shadowing of the variable
sub (ArrU v' ts) = ArrU v' (map sub ts)
sub (NamU namType v' ts rs) = NamU namType v' (map sub ts) [(x, sub t') | (x, t') <- rs]
sub (ExistU v' ps ds) = ExistU v' (map sub ps) (map sub ds)
free :: UnresolvedType -> Set.Set UnresolvedType
free v@(VarU _) = Set.singleton v
free v@(ExistU _ [] _) = Set.singleton v
free (ExistU v ts _) = Set.unions $ Set.singleton (ArrU v ts) : map free ts
free (FunU t1 t2) = Set.union (free t1) (free t2)
free (ForallU v t) = Set.delete (VarU v) (free t)
free (ArrU _ xs) = Set.unions (map free xs)
free (NamU _ _ _ rs) = Set.unions [free t | (_, t) <- rs]
-- Types are partially ordered, 'forall a . a' is lower (more generic) than
-- Int. But 'forall a . a -> a' cannot be compared to 'forall a . a', since
-- they are different kinds.
-- The order of types is used to choose the most specific serialization functions.
-- As far as serialization is concerned, properties and constraints do not matter.
instance P.PartialOrd UnresolvedType where
(<=) (VarU v1) (VarU v2) = v1 == v2
(<=) (ExistU v1 [] _) (ExistU v2 [] _) = v1 == v2
(<=) (ExistU v1 ts1 _) (ExistU v2 ts2 _)
= v1 == v2
&& length ts1 == length ts2
&& foldl (&&) True (zipWith (P.<=) ts1 ts2)
(<=) (FunU t11 t12) (FunU t21 t22)
= (P.<=) t11 t21
&& (P.<=) t22 t12
(<=) (ArrU v1 []) (ArrU v2 []) = v1 == v2
(<=) (ArrU v1 ts1) (ArrU v2 ts2)
= v1 == v2
&& length ts1 == length ts2
&& foldl (&&) True (zipWith (P.<=) ts1 ts2)
(<=) (NamU _ v1 _ es1) (NamU _ v2 _ es2)
= v1 == v2
&& length ts1 == length ts2
&& foldl (&&) True (zipWith (P.<=) ts1 ts2)
where
ts1 = map snd es1
ts2 = catMaybes $ map (\(k,_) -> lookup k es2) es1
(<=) (ForallU v t1) t2
| (P.==) (ForallU v t1) t2 = True
| otherwise = (P.<=) (substituteFirst v t1 t2) t2
(<=) _ _ = False
(==) (ForallU v1 t1) (ForallU v2 t2) =
if Set.member (VarU v1) (free t2)
then
let v = getNewVariable t1 t2
in (P.==) (substitute v1 (VarU v) t1) (substitute v2 (VarU v) t2)
else (P.==) t1 (substitute v2 (VarU v1) t2)
(==) a b = a == b
-- Substitute all v for the first term in t2 that corresponds to v in t1. If v
-- does not occur in t1, then t1 is returned unchanged (e.g., `forall a . Int`).
substituteFirst :: TVar -> UnresolvedType -> UnresolvedType -> UnresolvedType
substituteFirst v t1 t2 = case findFirst v t1 t2 of
(Just t) -> substitute v t t1
Nothing -> t1
-- | get a fresh variable name that is not used in t1 or t2
getNewVariable :: UnresolvedType -> UnresolvedType -> TVar
getNewVariable t1 t2 = findNew variables (Set.union (allVars t1) (allVars t2))
where
variables = [1 ..] >>= flip replicateM ['a' .. 'z']
findNew :: [String] -> Set.Set UnresolvedType -> TVar
findNew [] _ = error "Could not fresh variable in an infinite list ... odd"
findNew (x:xs) ts
| Set.member (VarU v) ts = findNew xs ts
| otherwise = v
where
v = TV (langOf t1) (MT.pack x)
allVars :: UnresolvedType -> Set.Set UnresolvedType
allVars (ForallU v t) = Set.union (Set.singleton (VarU v)) (allVars t)
allVars t = free t
findFirst :: TVar -> UnresolvedType -> UnresolvedType -> Maybe UnresolvedType
findFirst v (VarU v') t2
| v == v' = Just t2
| otherwise = Nothing
findFirst v (ForallU v1 t1) (ForallU v2 t2)
| v == v1 = Nothing
| otherwise = findFirst v t1 (substitute v2 (VarU v1) t2)
findFirst v (ForallU v1 t1) t2
| v == v1 = Nothing
| otherwise = findFirst v (substitute v1 (VarU v1) t1) t2
findFirst v (FunU t11 t12) (FunU t21 t22)
= case (findFirst v t11 t21, findFirst v t12 t22) of
(Just t, _) -> Just t
(_, Just t) -> Just t
_ -> Nothing
findFirst v (ArrU _ ts1) (ArrU _ ts2)
= listToMaybe . catMaybes $ zipWith (findFirst v) ts1 ts2
findFirst v (NamU _ _ _ es1) (NamU _ _ _ es2)
= listToMaybe . catMaybes $ zipWith (findFirst v) ts1 ts2
where
ts1 = map snd es1
ts2 = catMaybes $ map (\(k,_) -> lookup k es2) es1
findFirst _ _ _ = Nothing
-- | is t1 a generalization of t2?
isSubtypeOf :: UnresolvedType -> UnresolvedType -> Bool
isSubtypeOf t1 t2 = case P.compare t1 t2 of
(Just x) -> x <= EQ
_ -> False
equivalent :: UnresolvedType -> UnresolvedType -> Bool
equivalent t1 t2 = isSubtypeOf t1 t2 && isSubtypeOf t2 t1
-- | find all types that are not greater than any other type
mostGeneral :: [UnresolvedType] -> [UnresolvedType]
mostGeneral ts = P.minima ts
-- | find all types that are not less than any other type
mostSpecific :: [UnresolvedType] -> [UnresolvedType]
mostSpecific ts = P.maxima ts
-- | find the most specific subtypes
mostSpecificSubtypes :: UnresolvedType -> [UnresolvedType] -> [UnresolvedType]
mostSpecificSubtypes t ts = mostSpecific $ filter (\t2 -> isSubtypeOf t2 t) ts