{-# LANGUAGE GeneralizedNewtypeDeriving, ParallelListComp, PatternGuards, RankNTypes, RelaxedPolyRec #-} module TypeRel ( -- * Type operations -- ** Equality and subtyping AType(..), subtype, jointype, -- ** Queries and conversions qualConst, abstractTyCon, -- ** Tycon substitutions TyConSubst, makeTyConSubst, applyTyConSubst, applyTyConSubstInTyCon, replaceTyCon, replaceTyCons, substTyCons, substTyCon, -- * Tests tests, ) where import Env import ErrorST import Type import Util import Viewable import qualified Control.Monad.Reader as CMR import Data.Generics (Data, everywhere, mkT, extT) import Data.Monoid import qualified Data.Map as M import qualified Data.Set as S import qualified Test.HUnit as T -- | Remove the concrete portions of a type constructor. abstractTyCon :: TyCon -> TyCon abstractTyCon tc = tc { tcCons = ([], empty), tcNext = Nothing } -- | A substitution mapping type constructors to other type -- constructors newtype TyConSubst = TyConSubst { unTyConSubst :: M.Map Int TyCon } deriving Monoid -- | Construct a tycon substitution from a list of tycons and a list -- to map them to. makeTyConSubst :: [TyCon] -> [TyCon] -> TyConSubst makeTyConSubst tcs tcs' = TyConSubst (M.fromList [ (tcId tc, tc') | tc <- tcs | tc' <- tcs' ]) -- | Apply a tycon substitution to any SYB data. applyTyConSubst :: Data a => TyConSubst -> a -> a applyTyConSubst subst = loop where loop :: Data a => a -> a loop = everywhere (mkT tycon `extT` tyapp) -- tycon :: TyCon -> TyCon tycon tc | Just tc' <- M.lookup (tcId tc) (unTyConSubst subst) = applyTyConSubstInTyCon subst tc' | otherwise = tc -- tyapp :: Type -> Type tyapp (TyApp tc ts _) = tyApp tc ts tyapp t = t -- | Apply a tycon substitution "inside" the right-hand side of -- a tycon, but don't replace the tycon itself. applyTyConSubstInTyCon :: TyConSubst -> TyCon -> TyCon applyTyConSubstInTyCon subst tc = tc { tcNext = applyTyConSubst subst (tcNext tc), tcCons = applyTyConSubst subst (tcCons tc) } -- | Given a list of type constructors and something traversable, -- find all constructors with the same identity as the given type one, and -- replace them. We can use this for type abstraction by redacting -- data constructor or synonym expansions. It also replaces within -- the list of type constructors themselves, which ties the knot for -- recursive type constructors. replaceTyCons :: Data a => [TyCon] -> a -> a replaceTyCons tcs0 = substTyCons tcs0 tcs0 replaceTyCon :: Data a => TyCon -> a -> a replaceTyCon tc = replaceTyCons [tc] -- Give a list of tycons to replace and a list of tycons to replace them -- with, replaces them all recursively, including knot-tying substTyCons :: Data a => [TyCon] -> [TyCon] -> a -> a substTyCons tcs tcs' = applyTyConSubst (makeTyConSubst tcs tcs') -- | Replace all occurrences of the first tycon with the second substTyCon :: Data a => TyCon -> TyCon -> a -> a substTyCon tc tc' = substTyCons [tc] [tc'] -- | The constant bound on the qualifier of a type qualConst :: Type -> QLit qualConst = qConstBound . qualifier -- | A fresh type for defining alpha equality up to mu. newtype AType = AType { unAType :: Type } -- | On AType, we define simple alpha equality, up to mu and operator -- reduction, which we then use -- to keep track of where we've been when we define type equality -- that understands mu and reduction. instance Eq AType where te1 == te2 = compare te1 te2 == EQ instance Ord AType where te1 `compare` te2 = unAType te1 =?= unAType te2 where (=?=) :: Type -> Type -> Ordering TyApp tc ts _ =?= TyApp tc' ts' _ = tc `compare` tc' `thenCmp` map AType ts `compare` map AType ts' TyVar x =?= TyVar x' = x `compare` x' TyFun q t1 t2 =?= TyFun q' t1' t2' = q `compare` q' `thenCmp` t1 =?= t1' `thenCmp` t2 =?= t2' TyQu u x t =?= TyQu u' x' t' = u `compare` u' `thenCmp` tvqual x `compare` tvqual x' `thenCmp` tysubst x a t =?= tysubst x' a t' where a = TyVar (fastFreshTyVar x (maxtv (t, t'))) TyMu x t =?= TyMu x' t' = tvqual x `compare` tvqual x' `thenCmp` tysubst x a t =?= tysubst x' a t' where a = TyVar (fastFreshTyVar x (maxtv (t, t'))) TyApp _ _ _ =?= _ = LT _ =?= TyApp _ _ _ = GT TyVar _ =?= _ = LT _ =?= TyVar _ = GT TyFun _ _ _ =?= _ = LT _ =?= TyFun _ _ _ = GT TyQu _ _ _ =?= _ = LT _ =?= TyQu _ _ _ = GT type UT s t a = CMR.ReaderT (TCS s t) (ST t String) a -- | An environment mapping mu-bound type variables to their -- definition for unrolling ('Left') or forall-bound variables -- to a pair of lower and upper bounds, for instantiation ('Right') type UEnv t = M.Map TyVarR (UVar t) type UVar t = (Int, STRef t (Type, Type)) data TCS s t = TCS { -- | Pairs of types previously seen, and thus considered related -- if seen again. tcsSeen :: STRef t (M.Map (AType, AType) s), -- | A supply of fresh type variables tcsSupply :: STRef t [QLit -> TyVarR], -- | The number of instantiated foralls we are currently under tcsLevel :: Int, -- | The environment for the left side of the relation tcsEnv1 :: UEnv t, -- | The environment for the right side of the relation tcsEnv2 :: UEnv t } data Field s t = Field { get :: TCS s t -> UEnv t, update :: TCS s t -> UEnv t -> TCS s t } env1, env2 :: Field s t env1 = Field tcsEnv1 (\tcs e -> tcs { tcsEnv1 = e }) env2 = Field tcsEnv2 (\tcs e -> tcs { tcsEnv2 = e }) lift :: (CMR.MonadTrans t, Monad m) => m a -> t m a lift = CMR.lift runUT :: forall s a m. Monad m => (forall t. UT s t a) -> S.Set TyVarR -> m a runUT m set = either fail return $ runST $ do seen <- newTransSTRef M.empty supply <- newSTRef [ f | f <- tvalphabet , f Qu `S.notMember` set , f Qa `S.notMember` set ] CMR.runReaderT m TCS { tcsSeen = seen, tcsSupply = supply, tcsLevel = 1, tcsEnv1 = M.empty, tcsEnv2 = M.empty } getVar :: TyVarR -> Field s t -> UT s t (Maybe (UVar t)) getVar tv field = CMR.asks (M.lookup tv . get field) -- | To add some unification variables to the scope, run the body, -- and return a map containing their lower and upper bounds. -- Unification variables are assumed to be fresh with respect to -- existing variables. In particular, the initial set of unification -- variables precedes any other bindings, and all subsequent foralls -- are renamed using fresh type variables. withUVars :: [TyVarR] -> Field s t -> UT s t a -> UT s t (a, [Type]) withUVars tvs field body = do level <- CMR.asks tcsLevel refs <- lift $ sequence [ do ref <- newTransSTRef (tyBot, tyTop (tvqual tv)) return (tv, (level, ref)) | tv <- tvs ] res <- CMR.local (\st -> update field st (M.fromList refs `M.union` get field st)) body typs <- sequence [ do (lower, upper) <- lift $ readSTRef ref if lower <: upper then return $ -- This is a heuristic -- we prefer to return something -- with information, meaning not top or bottom, but if -- the choice is between top and bottom, we go with bottom if isBotType lower then if upper == tyUn || upper == tyAf then lower else upper else lower else fail $ "Unification cannot solve:\n" ++ show lower ++ " <: " ++ show upper | (_, (_, ref)) <- refs ] return (res, typs) -- | Bump up the quantification nesting level incU :: UT s t a -> UT s t a incU = CMR.local (\st -> st { tcsLevel = tcsLevel st + 1 }) -- | Try to assert an upper bound on a unification variable. upperBoundUVar :: STRef t (Type, Type) -> Type -> UT s t () upperBoundUVar ref t = do (lower, upper) <- lift $ readSTRef ref unless (upper <: t) $ do upper' <- t /\? upper lift $ writeSTRef ref (lower, upper') -- | Try to assert a lower bound on a unification variable. lowerBoundUVar :: STRef t (Type, Type) -> Type -> UT s t () lowerBoundUVar ref t = do (lower, upper) <- lift $ readSTRef ref unless (t <: lower) $ do lower' <- t \/? lower lift $ writeSTRef ref (lower', upper) -- | Get maps of the left and right uvars getUVars :: UT s t (TyVarR -> Maybe (Int, STRef t (Type, Type)), TyVarR -> Maybe (Int, STRef t (Type, Type))) getUVars = do st <- CMR.ask return (flip M.lookup (tcsEnv1 st), flip M.lookup (tcsEnv2 st)) -- | Check if two types have been seen before. If so, return the -- previously stored answer. If not, temporarily store the given -- answer, then run a block, and finally replace the stored answer -- with the result of the block. chkU :: Type -> Type -> s -> UT s t s -> UT s t s chkU t1 t2 s body = do st <- CMR.ask let key = (AType t2, AType t1) ref = tcsSeen st seen <- lift $ readSTRef ref case M.lookup key seen of Just s' -> return s' Nothing -> do lift $ modifySTRef ref (M.insert key s) res <- body lift $ modifySTRef ref (M.insert key res) return res -- | Flip the left and right sides of the relation in the given block. flipU :: UT s t a -> UT s t a flipU body = CMR.local flipSt body where flipSt (TCS seen level supply e1 e2) = TCS seen level supply e2 e1 -- | Get a fresh type variable from the supply. freshU :: QLit -> UT s t TyVarR freshU qlit = do ref <- CMR.ask >>! tcsSupply f:supply <- lift $ readSTRef ref lift $ writeSTRef ref supply return (f qlit) -- | Print a debug message -- pM :: Show b => b -> UT s t () -- pM = lift . unsafeIOToST . print -- pM = const $ return () subtype :: Monad m => Int -> [TyVarR] -> Type -> [TyVarR] -> Type -> m ([Type], [Type]) subtype limit uvars1 t1i uvars2 t2i = runUT start (S.fromList uvars1 `S.union` S.fromList uvars2 `S.union` alltv (t1i, t2i)) where start :: UT () t ([Type], [Type]) start = liftM (first snd) $ withUVars uvars2 env2 $ withUVars uvars1 env1 $ cmp t1i t2i -- cmp :: Type -> Type -> UT () t () cmp t u = chkU t u () $ case (t, u) of -- Handle top (_ , TyApp tcu _ _) | tcu == tcUn && qualConst t <: Qu -> return () (_ , TyApp tcu _ _) | tcu == tcAf -> return () -- Handle bottom (TyApp tct _ _, _) | tct == tcBot -> return () -- Variables (TyVar vt, TyVar vu) -> do mt' <- getVar vt env1 mu' <- getVar vu env2 case (mt', mu') of (Just (_, t'), Nothing) -> upperBoundUVar t' u (Nothing, Just (_, u')) -> lowerBoundUVar u' t (Just (lt, t'), Just (lu, u')) | lt > lu -> upperBoundUVar t' u | lt < lu -> lowerBoundUVar u' t _ -> unless (vt == vu) $ giveUp t u (TyVar vt, _) -> do mt' <- getVar vt env1 case mt' of Just (_, t') -> upperBoundUVar t' u Nothing -> giveUp t u (_, TyVar vu) -> do mu' <- getVar vu env2 case mu' of Just (_, u') -> lowerBoundUVar u' t Nothing -> giveUp t u -- Type applications (TyApp tct ts _, TyApp tcu us _) | tct == tcu, isHeadNormalType t, isHeadNormalType u -> cmpList (tcArity tct) ts us (TyApp tct ts _, TyApp tcu us _) | tct == tcu -> cmpList (tcArity tct) ts us `catchError` \_ -> do t' <- hn t u' <- hn u cmp t' u' (TyApp _ _ _, _) | not (isHeadNormalType t) -> (`cmp` u) =<< hn t (_, TyApp _ _ _) | not (isHeadNormalType u) -> (t `cmp`) =<< hn u -- Arrows (TyFun qt t1 t2, TyFun qu u1 u2) -> do subkind qt qu $ giveUp t u revCmp t1 u1 cmp t2 u2 -- Quantifiers (TyQu Forall tvt t1, _) -> do tv' <- freshU (tvqual tvt) incU $ withUVars [tv'] env1 $ cmp (tysubst tvt (TyVar tv') t1) u return () (_, TyQu Exists tvu u1) -> do tv' <- freshU (tvqual tvu) incU $ withUVars [tv'] env2 $ cmp t (tysubst tvu (TyVar tv') u1) return () (_, TyQu Forall tvu u1) -> do tv' <- freshU (tvqual tvu) cmp t (tysubst tvu (TyVar tv') u1) (TyQu Exists tvt t1, _) -> do tv' <- freshU (tvqual tvt) cmp (tysubst tvt (TyVar tv') t1) u -- Recursion (TyMu tvt t1, _) -> cmp (tysubst tvt t t1) u (_, TyMu tvu u1) -> cmp t (tysubst tvu u u1) -- Failure _ -> giveUp t u -- giveUp t u = fail $ "Got type `" ++ show t ++ "' where type `" ++ show u ++ "' expected" -- revCmp u t = flipU (cmp t u) -- hn t = headNormalizeTypeM limit t -- cmpList arity ts us = sequence_ [ case var of 1 -> cmp tj uj -1 -> revCmp tj uj _ -> do cmp tj uj; revCmp tj uj | var <- arity | tj <- ts | uj <- us ] -- subkind qd1 qd2 orElse = if qd1 <: qd2 then return () else do (m1, m2) <- getUVars case (view $ qRepresent qd1, view $ qRepresent qd2) of (QeVar tv1, QeVar tv2) | Just (_, ref) <- m1 tv1, Nothing <- m2 tv2 -> upperBoundUVar ref (TyVar tv2) | Nothing <- m1 tv1, Just (_, ref) <- m2 tv2 -> lowerBoundUVar ref (TyVar tv1) (QeVar tv1, QeLit qlit) | Just (_, ref) <- m1 tv1 -> upperBoundUVar ref (tyTop qlit) (QeLit qlit, QeVar tv2) | Just (_, ref) <- m2 tv2 -> lowerBoundUVar ref (tyTop qlit) _ -> orElse jointype :: Monad m => Int -> Bool -> Type -> Type -> m Type jointype limit b t1i t2i = liftM clean $ runUT (cmp (b, True) t1i t2i) (alltv (t1i, t2i)) where cmp, revCmp :: (Bool, Bool) -> Type -> Type -> UT Type t Type cmp m t u = do let (direction, _) = m tv <- freshU (qualConst t \/ qualConst u) catchTop m t u $ chkU t u (TyVar tv) $ TyMu tv `liftM` case (t, u) of -- Handle top and bottom _ | Just t' <- points direction t u -> return t' | Just t' <- points direction u t -> return t' -- Type applications (TyApp tct ts _, TyApp tcu us _) | tct == tcu, isHeadNormalType t, isHeadNormalType u -> tyApp tct `liftM` cmpList (tcArity tct) (direction, True) ts us (TyApp tct ts _, TyApp tcu us _) | tct == tcu -> liftM (tyApp tct) (cmpList (tcArity tct) (direction, False) ts us) `catchError` \_ -> do t' <- hn t u' <- hn u cmp m t' u' (TyApp _ _ _, _) | not (isHeadNormalType t) -> do t' <- hn t cmp m t' u (_, TyApp _ _ _) | not (isHeadNormalType u) -> do u' <- hn u cmp m t u' -- Variables (TyVar vt, TyVar ut) | vt == ut -> return t -- Arrows (TyFun qt t1 t2, TyFun qu u1 u2) -> do q' <- ifMJ direction qt qu t1' <- revCmp m t1 u1 t2' <- cmp m t2 u2 return (TyFun q' t1' t2') -- Quantifiers (TyQu qt tvt t1, TyQu qu tvu u1) | qt == qu -> do q' <- ifMJ direction (tvqual tvt) (tvqual tvu) tv' <- freshU q' liftM (TyQu qt tv') $ cmp m (tysubst tvt (TyVar tv') t1) (tysubst tvu (TyVar tv') u1) -- Recursion (TyMu tvt t1, _) -> cmp m (tysubst tvt t t1) u (_, TyMu tvu u1) -> cmp m t (tysubst tvu u u1) -- Failure _ -> fail $ "Could not " ++ (if direction then "join" else "meet") ++ " types `" ++ show t ++ "' and `" ++ show u ++ "'" -- hn t = headNormalizeTypeM limit t -- cmpList arity m ts us = sequence [ case var of 1 -> cmp m tj uj -1 -> revCmp m tj uj _ -> if tj == uj then return tj else fail $ "Could not unify types `" ++ show tj ++ "' and `" ++ show uj ++ "'" | var <- arity | tj <- ts | uj <- us ] -- points True t u@(TyApp tc _ _) | tc == tcAf = Just u | tc == tcUn, qualConst t <: Qu = Just u | tc == tcBot = Just t points False t u@(TyApp tc _ _) | tc == tcAf = Just t | tc == tcUn, qualConst t <: Qu = Just t | tc == tcBot = Just u points _ _ _ = Nothing -- revCmp (direction, lossy) t u = cmp (not direction, lossy) t u -- catchTop (True, True) t u body = body `catchError` \_ -> return (tyTop (qualConst t \/ qualConst u)) {- catchTop (False, True) _ _ body = body `catchError` \_ -> return tyBot -} catchTop _ _ _ body = body -- clean :: Type -> Type clean (TyApp tc ts _) = tyApp tc (map clean ts) clean (TyVar a) = TyVar a clean (TyFun q t1 t2) = TyFun q (clean t1) (clean t2) clean (TyQu u a t) = TyQu u a (clean t) clean (TyMu a t) | a `S.member` ftv t = TyMu a (clean t) | otherwise = clean t -- | Helper to force 'Either' to the right type runEither :: (String -> r) -> (a -> r) -> Either String a -> r runEither = either -- | The Type partial order instance Eq Type where t1 == t2 = t1 <: t2 && t2 <: t1 instance PO Type where t1 <: t2 = runEither (const False) (const True) (subtype 100 [] t1 [] t2) ifMJ b t1 t2 = jointype 100 b t1 t2 subtypeTests, joinTests, uvarsTests :: T.Test subtypeTests = T.test [ tyUnit <:! tyUnit , tyUnit !<: tyInt , tyInt <:! tyInt , tyInt .->. tyInt <:! tyInt .->. tyInt , tyInt .->. tyInt <:! tyInt .-*. tyInt , tyInt .-*. tyInt <:! tyInt .-*. tyInt , tyInt .-*. tyInt !<: tyInt .->. tyInt , tyUnit .->. tyInt !<: tyInt .->. tyInt , (tyInt .-*. tyInt) .->. tyInt .->. tyInt <:! (tyInt .->. tyInt) .->. tyInt .-*. tyInt , tyInt .->. tyInt <:! tyUn , tyInt .->. tyInt <:! tyAf , tyInt .-*. tyInt !<: tyUn , tyInt .-*. tyInt <:! tyAf , tyUn <:! tyAf , tyAf !<: tyUn , tyRecv tyInt <:! tyRecv tyInt , tyRecv tyInt !<: tyRecv tyUnit , tyRecv tyInt !<: tySend tyInt , tyRecv (tyInt .-*. tyInt) <:! tyRecv (tyInt .->. tyInt) , tyRecv (tyInt .->. tyInt) !<: tyRecv (tyInt .-*. tyInt) , tySend (tyInt .-*. tyInt) !<: tySend (tyInt .->. tyInt) , tySend (tyInt .->. tyInt) <:! tySend (tyInt .-*. tyInt) , tyIdent tyInt <:! tyIdent tyInt , tyIdent tyInt !<: tyIdent tyUnit , tyInt <:! tyIdent tyInt , tyIdent tyInt <:! tyInt , tyInt !<: tyIdent tyUnit , tyIdent tyInt !<: tyUnit , tyConst tyInt <:! tyConst tyInt , tyConst tyInt <:! tyConst tyUnit , tyConst tyInt <:! tyUnit , tyUnit <:! tyConst tyInt , tyUnit .->. tyInt <:! tyIdent (tyConst (tySend tyInt) .-*. tyInt) , tyInt .->. tyInt !<: tyIdent (tyConst (tySend tyInt) .-*. tyInt) , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) <:! tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) <:! tySend tyInt .:. tyDual (tySend tyUnit .:. tyUnit) , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) <:! tySend tyInt .:. tyRecv tyUnit .:. tyUnit , tyBot <:! tyInt .->. tyInt , tyInt .->. tyInt !<: tyBot , TyVar a <:! TyVar a , TyVar a !<: TyVar b , tyAll a (tyInt .->. TyVar a) <:! tyAll b (tyInt .->. TyVar b) , tyAll a (tyInt .->. TyVar a) <:! tyAll b (tyInt .->. TyVar a) , tyAll c (TyVar c .->. tyInt) <:! tyAll a (TyVar a .-*. tyInt) , tyAll a (TyVar a .->. tyInt) !<: tyAll c (TyVar c .-*. tyInt) , tyAll a (tyAll b (TyVar a .*. TyVar b)) <:! tyAll b (tyAll a (TyVar b .*. TyVar a)) , tyAll a (tyAll b (TyVar a .*. TyVar b)) <:! tyAll b (tyAll a (TyVar a .*. TyVar b)) , tyAll a (tyAll a (TyVar a .*. TyVar b)) !<: tyAll b (tyAll a (TyVar a .*. TyVar b)) , tyAll a (tyAll a (TyVar a .*. TyVar b)) <:! tyAll a (tyAll a (TyVar a .*. TyVar b)) , TyMu a (tyInt .->. TyVar a) <:! TyMu b (tyInt .->. TyVar b) , TyMu a (tyInt .->. TyVar a) <:! TyMu b (tyInt .->. tyInt .->. TyVar b) , TyMu a (tyInt .->. TyVar a) <:! TyMu b (tyInt .->. tyInt .-*. TyVar b) , TyMu a (tyInt .->. TyVar a) !<: TyMu b (tyInt .->. tyUnit .-*. TyVar b) , TyMu a (TyVar a .*. tyInt .*. tyInt) <:! TyMu a (TyVar a .*. tyInt .*. tyInt) .*. tyInt , TyMu a (TyVar a .*. tyInt .*. tyUnit) <:! TyMu a (TyVar a .*. tyUnit .*. tyInt) .*. tyUnit , tyAll c (TyMu a (TyVar a .*. tyInt .*. TyVar c)) <:! tyAll d (TyMu a (TyVar a .*. TyVar d .*. tyInt) .*. TyVar d) , tyAll c (TyMu a (TyVar a .*. tyInt .*. TyVar c)) !<: tyAll d (TyMu a (TyVar d .*. TyVar a .*. tyInt) .*. TyVar d) , TyMu a (tyAll c ((tyInt .-*. TyVar c) .->. TyVar a)) !<: TyMu b (tyAll d ((tyInt .->. TyVar d) .->. TyVar c)) , TyMu a (tyAll c (tyInt .-*. TyVar c) .->. TyVar a) <:! TyMu b (tyAll d (tyInt .->. TyVar d) .->. TyVar b) , TyMu a (tyAll c (TyVar a .-*. TyVar c) .->. TyVar a) <:! TyMu b (tyAll d (TyVar b .->. TyVar d) .->. TyVar b) , tyAll a (TyVar a .*. tyInt) .->. TyVar a <:! tyAll b (TyVar b .*. tyInt) .->. TyVar a , tyAll a (TyVar a .*. tyInt) .->. TyVar a !<: tyAll b (TyVar b .*. tyInt) .->. TyVar b -- Universal instantiation tests , tyAll a (TyVar a .->. TyVar a) <:! tyInt .->. tyInt , tyAll a (TyVar a .->. TyVar a) !<: tyInt .->. tyUnit , tyInt .->. tyInt !<: tyAll a (TyVar a .->. TyVar a) , tyAll a (TyVar a .->. tyInt) <:! tyInt .->. tyInt , tyAll a (tyInt .->. tyInt) <:! tyInt .->. tyInt , tyInt .->. tyAll a (TyVar a .->. TyVar a) <:! tyInt .->. tyInt .->. tyInt , TyMu a (TyVar a .*. (tyAll a (TyVar a .->. TyVar a))) <:! TyMu a (TyVar a .*. (tyInt .->. tyInt)) , TyMu a (TyVar a .*. (tyAll a (tyInt .->. TyVar a))) <:! TyMu a (TyVar a .*. (tyInt .->. tyInt)) , TyMu b (TyVar b .*. (tyAll a (TyVar a .->. TyVar a))) <:! TyMu a (TyVar a .*. (tyInt .->. tyInt)) , TyMu b (TyVar b .*. (tyAll a (tyInt .->. TyVar a))) <:! TyMu a (TyVar a .*. (tyInt .->. tyInt)) , TyMu a (tyAll b (TyVar b .->. TyVar a)) <:! TyMu a (tyInt .->. TyVar a) , tyAll a (TyVar a .*. tyInt) <:! TyMu a (TyVar a .*. tyInt) , tyAll a (TyVar a .*. TyVar a) !<: TyMu a (TyVar a .*. tyInt) , tyAll a (TyMu b (TyVar a .->. TyVar b)) <:! TyMu b (tyInt .->. TyVar b) , tyAll a (TyMu a (tyInt .->. TyVar a)) !<: TyMu b (tyInt .->. tyInt) , tyAll a (tyInt .->. TyVar a) .->. tyInt !<: (tyInt .->. tyInt) .->. tyInt , (tyInt .->. tyInt) .->. tyInt <:! tyAll a (tyInt .->. TyVar a) .->. tyInt , tyAll a (tyInt .->. TyVar a) !<: tyInt .->. tyInt .-*. tyInt -- This is now true, but should it be?: , TyMu a (tyAll c (tyInt .->. tyAll d (TyVar c .->. TyVar a))) <:! tyAll c (tyInt .->. TyMu a (tyAll d (TyVar c .->. tyAll c (tyInt .->. TyVar a)))) -- This is now true, but should it be?: , tyAll c (tyInt .->. TyMu a (tyAll d (TyVar c .->. tyAll c (tyInt .->. TyVar a)))) <:! TyMu a (tyAll c (tyInt .->. tyAll d (TyVar c .->. TyVar a))) , tyInt <:! tyEx a (TyVar a) , tyInt <:! tyEx a tyInt , tyInt .*. tyInt <:! tyEx a (TyVar a .*. tyInt) , tyInt .*. tyInt <:! tyEx a (tyInt .*. TyVar a) , tyInt .*. tyInt <:! tyEx a (TyVar a .*. TyVar a) , tyInt .*. tyInt <:! tyEx a (tyEx b (TyVar a .*. TyVar a)) , tyInt .*. tyInt <:! tyEx a (tyEx b (TyVar b .*. TyVar a)) , tyUn .->. tyUn !<: TyVar a .->. TyVar a -- These are potentially sketchy, but useful: , tyInt <:! tyAll a tyInt , tyInt !<: tyAll a (TyVar a) , tyEx a tyInt <:! tyInt , tyEx a (TyVar a) !<: tyInt , tyEx a (TyVar a) !<: TyVar a ] where t1 <:! t2 = T.assertBool (show t1 ++ " <: " ++ show t2) (t1 <: t2) t1 !<: t2 = T.assertBool (show t1 ++ " /<: " ++ show t2) (t1 /<: t2) infix 4 <:!, !<: a = tvUn "a"; b = tvUn "b"; c = tvAf "c"; d = tvAf "d" joinTests = T.test [ tyUnit \/! tyUnit ==! tyUnit , tyUnit /\! tyUnit ==! tyUnit , tyInt /\! tyInt ==! tyInt , tyUnit \/! tyInt ==! tyUn , tyUnit !/\ tyInt , tyInt .->. tyInt \/! tyInt .->. tyInt ==! tyInt .->. tyInt , tyInt .->. tyInt \/! tyInt .-*. tyInt ==! tyInt .-*. tyInt , tyInt .-*. tyInt \/! tyInt .-*. tyInt ==! tyInt .-*. tyInt , tyInt .-*. tyInt \/! tyInt .->. tyInt ==! tyInt .-*. tyInt , tyInt .->. tyInt /\! tyInt .->. tyInt ==! tyInt .->. tyInt , tyInt .->. tyInt /\! tyInt .-*. tyInt ==! tyInt .->. tyInt , tyInt .-*. tyInt /\! tyInt .-*. tyInt ==! tyInt .-*. tyInt , tyInt .-*. tyInt /\! tyInt .->. tyInt ==! tyInt .->. tyInt , tyInt .->. tyInt \/! tyInt .->. tyUnit ==! tyInt .->. tyUn , tyInt .->. tyInt \/! tyUnit .->. tyInt ==! tyUn , tyInt .-*. tyInt \/! tyUnit .->. tyInt ==! tyAf , tyInt .->. tyInt !/\ tyInt .->. tyUnit , tyInt .->. tyInt /\! tyUnit .->. tyInt ==! tyUn .->. tyInt , tyInt .-*. tyInt /\! tyUnit .->. tyInt ==! tyUn .->. tyInt , (tyInt .-*. tyInt) .-*. tyInt /\! tyUnit .->. tyInt ==! tyAf .->. tyInt , tyInt .->. tyInt \/! tyUn ==! tyUn , tyInt .->. tyInt \/! tyAf ==! tyAf , tyInt .-*. tyInt \/! tyUn ==! tyAf , tyInt .-*. tyInt \/! tyAf ==! tyAf , tyInt .->. tyInt /\! tyUn ==! tyInt .->. tyInt , tyInt .->. tyInt /\! tyAf ==! tyInt .->. tyInt , tyInt .-*. tyInt !/\ tyUn -- could do better , tyInt .-*. tyInt /\! tyAf ==! tyInt .-*. tyInt , tyRecv tyInt \/! tyRecv tyInt ==! tyRecv tyInt , tySend tyInt \/! tySend tyUnit ==! tySend tyUn , tyRecv tyInt \/! tySend tyInt ==! tyUn , tyRecv (tyInt .-*. tyInt) \/! tyRecv (tyInt .->. tyInt) ==! tyRecv (tyInt .->. tyInt) , tyRecv (tyInt .->. tyInt) \/! tyRecv (tyInt .-*. tyInt) ==! tyRecv (tyInt .->. tyInt) , tySend (tyInt .-*. tyInt) \/! tySend (tyInt .->. tyInt) ==! tySend (tyInt .-*. tyInt) , tySend (tyInt .->. tyInt) \/! tySend (tyInt .-*. tyInt) ==! tySend (tyInt .-*. tyInt) , tyRecv (tyInt .-*. tyInt) /\! tyRecv (tyInt .->. tyInt) ==! tyRecv (tyInt .-*. tyInt) , tyRecv (tyInt .->. tyInt) /\! tyRecv (tyInt .-*. tyInt) ==! tyRecv (tyInt .-*. tyInt) , tySend (tyInt .-*. tyInt) /\! tySend (tyInt .->. tyInt) ==! tySend (tyInt .->. tyInt) , tySend (tyInt .->. tyInt) /\! tySend (tyInt .-*. tyInt) ==! tySend (tyInt .->. tyInt) , tyIdent tyInt \/! tyIdent tyInt ==! tyIdent tyInt , tyIdent tyInt \/! tyIdent tyUnit ==! tyUn , tyInt \/! tyIdent tyInt ==! tyInt , tyInt \/! tyIdent tyUnit ==! tyUn , tyIdent tyInt /\! tyIdent tyInt ==! tyIdent tyInt , tyIdent tyInt !/\ tyIdent tyUnit , tyInt /\! tyIdent tyInt ==! tyInt , tyInt !/\ tyIdent tyUnit , tyIdent (tyIdent tyInt) \/! tyIdent tyInt ==! tyIdent tyInt , tyIdent (tyConst tyInt) \/! tyIdent (tyConst tyUnit) ==! tyIdent tyUnit , tyConst tyInt \/! tyConst tyInt ==! tyConst tyInt , tyConst tyInt \/! tyConst tyUnit ==! tyUnit , tyConst tyInt /\! tyConst tyInt ==! tyConst tyInt , tyConst tyInt /\! tyConst tyUnit ==! tyUnit , tyUnit .->. tyInt \/! tyIdent (tyConst (tySend tyInt) .-*. tyInt) ==! tyUnit .-*. tyInt , tyInt .->. tyInt \/! tyIdent (tyConst (tySend tyInt) .-*. tyInt) ==! tyAf , tyUnit .->. tyInt /\! tyIdent (tyConst (tySend tyInt) .-*. tyInt) ==! tyUnit .->. tyInt , tyInt .->. tyInt /\! tyIdent (tyConst (tySend tyInt) .-*. tyInt) ==! tyUn .->. tyInt , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) \/! tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) ==! tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) \/! tySend tyInt .:. tyDual (tySend tyUnit .:. tyUnit) ==! tySend tyInt .:. tyDual (tySend tyUnit .:. tyUnit) , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) \/! tySend tyInt .:. tyRecv tyUnit .:. tyUnit ==! tySend tyInt .:. tyRecv tyUnit .:. tyUnit , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) /\! tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) ==! tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) /\! tySend tyInt .:. tyDual (tySend tyUnit .:. tyUnit) ==! tySend tyInt .:. tyDual (tySend tyUnit .:. tyUnit) , tyDual (tyRecv tyInt .:. tySend tyUnit .:. tyUnit) /\! tySend tyInt .:. tyRecv tyUnit .:. tyUnit ==! tySend tyInt .:. tyRecv tyUnit .:. tyUnit , tyBot \/! tyInt .->. tyInt ==! tyInt .->. tyInt , tyInt .->. tyInt /\! tyBot ==! tyAll b (TyVar b) , TyVar a \/! TyVar a ==! TyVar a , TyVar a \/! TyVar b ==! tyUn , TyVar a \/! TyVar c ==! tyAf , TyVar a /\! TyVar a ==! TyVar a , TyVar a !/\ TyVar b , TyVar a !/\ TyVar c , tyAll a (tyInt .->. TyVar a) \/! tyAll b (tyInt .->. TyVar b) ==! tyAll a (tyInt .->. TyVar a) , tyAll a (tyInt .->. TyVar a) \/! tyAll b (tyInt .->. TyVar a) ==! tyAll a (tyInt .->. tyUn) , tyAll c (TyVar c .->. tyInt) \/! tyAll a (TyVar a .-*. tyInt) ==! tyAll d (TyVar d .-*. tyInt) , tyAll a (tyInt .->. TyVar a) /\! tyAll b (tyInt .->. TyVar b) ==! tyAll a (tyInt .->. TyVar a) , tyAll a (tyInt .->. TyVar a) !/\ tyAll b (tyInt .->. TyVar a) , tyAll c (TyVar c .->. tyInt) /\! tyAll a (TyVar a .-*. tyInt) ==! tyAll b (TyVar b .->. tyInt) , tyAll a (tyAll b (TyVar a .*. TyVar b)) \/! tyAll b (tyAll a (TyVar b .*. TyVar a)) ==! tyAll b (tyAll a (TyVar b .*. TyVar a)) , tyAll a (tyAll b (TyVar a .*. TyVar b)) \/! tyAll b (tyAll a (TyVar a .*. TyVar b)) ==! tyAll b (tyAll a (tyUn .*. tyUn)) , tyAll c (tyAll c (TyVar c .*. TyVar d)) \/! tyAll d (tyAll c (TyVar c .*. TyVar d)) ==! tyAll d (tyAll d (TyVar d .*. tyAf)) , tyAll a (tyAll a (TyVar a .*. TyVar b)) \/! tyAll a (tyAll a (TyVar a .*. TyVar b)) ==! tyAll a (tyAll a (TyVar a .*. TyVar b)) , tyAll a (tyAll b (TyVar a .*. TyVar b)) /\! tyAll b (tyAll a (TyVar b .*. TyVar a)) ==! tyAll b (tyAll a (TyVar b .*. TyVar a)) , tyAll a (tyAll b (TyVar a .*. TyVar b)) !/\ tyAll b (tyAll a (TyVar a .*. TyVar b)) , tyAll c (tyAll c (TyVar c .*. TyVar d)) !/\ tyAll d (tyAll c (TyVar c .*. TyVar d)) , tyAll a (tyAll a (TyVar a .*. TyVar b)) /\! tyAll a (tyAll a (TyVar a .*. TyVar b)) ==! tyAll a (tyAll a (TyVar a .*. TyVar b)) , TyMu a (tyInt .->. TyVar a) \/! TyMu b (tyInt .->. TyVar b) ==! TyMu b (tyInt .->. TyVar b) , TyMu a (tyInt .->. TyVar a) /\! TyMu b (tyInt .->. TyVar b) ==! TyMu b (tyInt .->. TyVar b) , TyMu a (tyInt .->. TyVar a) \/! TyMu b (tyInt .->. tyInt .->. TyVar b) ==! TyMu a (tyInt .->. TyVar a) , TyMu a (tyInt .->. TyVar a) /\! TyMu b (tyInt .->. tyInt .->. TyVar b) ==! TyMu a (tyInt .->. TyVar a) , TyMu a (tyInt .->. TyVar a) \/! TyMu b (tyInt .->. tyInt .-*. TyVar b) ==! TyMu b (tyInt .->. tyInt .-*. TyVar b) , TyMu a (tyInt .->. TyVar a) /\! TyMu b (tyInt .->. tyInt .-*. TyVar b) ==! TyMu b (tyInt .->. TyVar b) , TyMu a (tyInt .->. TyVar a) \/! TyMu b (tyInt .->. tyUnit .-*. TyVar b) ==! tyInt .->. tyAf , TyMu a (tyInt .->. TyVar a) /\! TyMu b (tyInt .->. tyUnit .-*. TyVar b) ==! TyMu a (tyInt .->. tyUn .->. TyVar a) , TyMu a (TyVar a .*. tyInt .*. tyInt) \/! TyMu a (TyVar a .*. tyInt .*. tyInt) .*. tyInt ==! TyMu a (TyVar a .*. tyInt) , TyMu a (TyVar a .*. tyInt .*. tyInt) /\! TyMu a (TyVar a .*. tyInt .*. tyInt) .*. tyInt ==! TyMu a (TyVar a .*. tyInt) , TyMu a (TyVar a .*. tyInt .*. tyUnit) \/! TyMu a (TyVar a .*. tyUnit .*. tyInt) .*. tyUnit ==! TyMu b (TyVar b .*. tyInt .*. tyUnit) , TyMu a (TyVar a .*. tyInt .*. tyUnit) /\! TyMu a (TyVar a .*. tyUnit .*. tyInt) .*. tyUnit ==! TyMu b (TyVar b .*. tyInt .*. tyUnit) , tyAll c (TyMu a (TyVar a .*. tyInt .*. TyVar c)) \/! tyAll d (TyMu a (TyVar a .*. TyVar d .*. tyInt) .*. TyVar d) ==! tyAll c (TyMu b (TyVar b .*. tyInt .*. TyVar c)) , tyAll c (TyMu a (TyVar a .*. tyInt .*. TyVar c)) /\! tyAll d (TyMu a (TyVar a .*. TyVar d .*. tyInt) .*. TyVar d) ==! tyAll c (TyMu b (TyVar b .*. tyInt .*. TyVar c)) , tyAll c (TyMu a (TyVar a .*. tyInt .*. TyVar c)) \/! tyAll d (TyMu a (TyVar d .*. TyVar a .*. tyInt) .*. TyVar d) ==! tyAll c (tyAf .*. tyAf .*. tyInt .*. TyVar c) , tyAll c (TyMu a (TyVar a .*. tyInt .*. TyVar c)) !/\ tyAll d (TyMu a (TyVar d .*. TyVar a .*. tyInt) .*. TyVar d) , TyMu a (tyAll c (tyInt .-*. TyVar c) .->. TyVar a) \/! TyMu b (tyAll d (tyInt .->. TyVar d) .->. TyVar c) ==! tyAll d (tyInt .->. TyVar d) .->. tyAf , TyMu a (tyAll c (tyInt .-*. TyVar c) .->. TyVar a) !/\ TyMu b (tyAll d (tyInt .->. TyVar d) .->. TyVar c) , TyMu a (tyAll c (tyInt .-*. TyVar c) .->. TyVar a) \/! TyMu b (tyAll d (tyInt .->. TyVar d) .->. TyVar b) ==! TyMu b (tyAll c (tyInt .->. TyVar c) .->. TyVar b) , TyMu a (tyAll c (tyInt .-*. TyVar c) .->. TyVar a) /\! TyMu b (tyAll d (tyInt .->. TyVar d) .->. TyVar b) ==! TyMu b (tyAll c (tyInt .-*. TyVar c) .->. TyVar b) , TyMu a (tyAll c (TyVar a .-*. TyVar c) .->. TyVar a) \/! TyMu b (tyAll d (TyVar b .->. TyVar d) .->. TyVar b) ==! TyMu b (tyAll d (TyVar b .->. TyVar d) .->. TyVar b) , TyMu a (tyAll c (TyVar a .-*. TyVar c) .->. TyVar a) /\! TyMu b (tyAll d (TyVar b .->. TyVar d) .->. TyVar b) ==! TyMu b (tyAll d (TyVar b .-*. TyVar d) .->. TyVar b) , tyAll a (TyVar a .*. tyInt) .->. TyVar a \/! tyAll b (TyVar b .*. tyInt) .->. TyVar a ==! tyAll b (TyVar b .*. tyInt) .->. TyVar a , tyAll a (TyVar a .*. tyInt) .->. TyVar a /\! tyAll b (TyVar b .*. tyInt) .->. TyVar a ==! tyAll b (TyVar b .*. tyInt) .->. TyVar a , tyAll a (TyVar a .*. tyInt) .->. TyVar a \/! tyAll b (TyVar b .*. tyInt) .->. TyVar b ==! tyAll b (TyVar b .*. tyInt) .->. tyUn , tyAll a (TyVar a .*. tyInt) .->. TyVar a !/\ tyAll b (TyVar b .*. tyInt) .->. TyVar b , tyBot \/! TyVar b ==! TyVar b , tyIdent tyBot \/! TyVar b ==! TyVar b ] where t1 \/! t2 = Left (t1, t2) t1 /\! t2 = Right (t1, t2) Left (t1, t2) ==! t = T.assertEqual (show t1 ++ " \\/ " ++ show t2 ++ " = " ++ show t) (Just t) (t1 \/? t2) Right (t1, t2) ==! t = T.assertEqual (show t1 ++ " /\\ " ++ show t2 ++ " = " ++ show t) (Just t) (t1 /\? t2) t1 !/\ t2 = T.assertEqual (show t1 ++ " /\\ " ++ show t2 ++ " DNE") Nothing (t1 /\? t2) infix 2 ==! infix 4 \/!, /\!, !/\ a = tvUn "a"; b = tvUn "b"; c = tvAf "c"; d = tvAf "d" uvarsTests = T.test [ tyInt !<: tyUnit , tyInt <:! tyInt ==! (noU, noU, noA, noA) , TyVar a <:! tyInt ==! (tyInt, noU, noA, noA) , TyVar c <:! tyInt ==! (noU, noU, tyInt, noA) , tyInt !<: TyVar a , TyVar a .*. TyVar a <:! tyInt .*. tyInt ==! (tyInt, noU, noA, noA) , TyVar a .*. TyVar a !<: tyInt .*. tyUnit , TyVar a .*. TyVar a <:! (tyInt .->. tyInt) .*. (tyInt .-*. tyInt) ==! (tyInt .->. tyInt, noU, noA, noA) , TyVar a .*. TyVar a <:! (tyUnit .->. tyInt) .*. (tyInt .-*. tyInt) ==! (tyUn .->. tyInt, noU, noA, noA) , TyVar a .->. tyInt <:! tyInt .->. tyInt ==! (tyInt, noU, noA, noA) , TyVar a .->. TyVar a <:! tyInt .->. tyInt ==! (tyInt, noU, noA, noA) , TyVar a .->. TyVar a !<: tyFloat .->. tyInt , TyVar a .->. TyVar a !<: (tyInt .->. tyInt) .-*. (tyInt .-*. tyInt) , TyVar c .->. TyVar c <:! (tyInt .->. tyInt) .-*. (tyInt .-*. tyInt) ==! (noU, noU, tyInt .->. tyInt, noA) , TyVar c .->. TyVar c !<: (tyInt .-*. tyInt) .-*. (tyInt .->. tyInt) , TyVar c .-*. TyVar c !<: (tyInt .->. tyInt) .->. (tyInt .-*. tyInt) , TyVar a .*. TyVar a <:! tyDual (tyRecv tyInt .:. tyUnit) .*. (tySend tyInt .:. tyUnit) ==! (tySend tyInt .:. tyUnit, noU, noA, noA) , TyVar a .*. TyVar a !<: tyDual (tyRecv tyInt .:. tyUnit) .*. (tySend tyInt .:. tyInt) , TyVar a .*. tyAll a (TyVar a .->. tyInt) <:! tyInt .*. tyAll b (TyVar b .->. tyInt) ==! (tyInt, noU, noA, noA) , TyVar a .*. tyAll a (TyVar a .->. tyInt) <:! tyInt .*. tyAll b (tyInt .->. tyInt) ==! (tyInt, noU, noA, noA) , tyAll a (TyVar a .->. tyInt) <:! tyAll a (tyInt .->. tyInt) ==! (noU, noU, noA, noA) , TyVar a <:! tyInt .->. TyMu a (tyInt .->. TyVar a) ==! (TyMu b (tyInt .->. TyVar b), noU, noA, noA) , TyVar a .->. TyVar b <:! tyInt .->. TyMu a (tyInt .->. TyVar a) ==! (tyInt, TyMu b (tyInt .->. TyVar b), noA, noA) , TyVar a .->. TyVar b <:! TyMu a (tyInt .->. TyVar a) ==! (tyInt, TyMu b (tyInt .->. TyVar b), noA, noA) , TyVar a >:! tyInt ==! (tyInt, noU, noA, noA) , TyVar a .-*. TyVar a >:! tyInt .->. tyInt ==! (tyInt, noU, noA, noA) , TyVar a .->. TyVar a !>: tyInt .-*. tyInt , TyVar a .-*. TyVar a >:! tyUn .->. tyInt ==! (tyInt, noU, noA, noA) , TyFun (qInterpret (qeVar c)) tyInt tyInt <:! tyInt .-*. tyInt ==! (noU, noU, noA, noA) , TyFun (qInterpret (qeVar c)) tyInt tyInt <:! tyInt .->. tyInt ==! (noU, noU, noA, noA) , (TyVar c .->. TyVar d .-*. TyVar d) .*. TyVar d .*. tyRecv (TyVar c) <:! (TyVar e .->. TyVar f .-*. TyVar f) .*. TyVar f .*. tyRecv (TyVar e) ==! (noU, noU, TyVar e, TyVar f) , tyConst (TyVar a) <:! tyConst (tyInt) ==! (tyInt, noU, noA, noA) -- suboptimal , tyConst (TyVar a .*. tyUnit) <:! tyConst (tyInt .*. tyInt) ==! (noU, noU, noA, noA) , tyRecv (TyVar c) .*. tyRecv (TyVar c) >:! tyRecv (TyVar e) .*. tyAll f (tyRecv (TyVar f)) ==! (noU, noU, TyVar e, noA) , tyRecv (TyVar c) .*. tyRecv (TyVar c) >:! tyRecv (TyVar e) .*. tyRecv (TyVar e) ==! (noU, noU, TyVar e, noA) , tyRecv (TyVar c) .*. tyRecv (TyVar c) !>: tyRecv (TyVar e) .*. tyRecv (TyVar f) , T.assertEqual "':! tyFollow tyUnit (tyRecv tyInt .:. TyMu e (tyFollow tyUnit (tyRecv tyInt .:. TyVar e))) ==! (tyUnit, (tyRecv tyInt .:. TyMu e (tyFollow tyUnit (tyRecv tyInt .:. TyVar e))), noA, noA) , tyFollow (TyVar a) (TyVar b) >:! TyMu e (tyFollow tyUnit (tyRecv tyInt .:. TyVar e)) ==! (tyUnit, (tyRecv tyInt .:. TyMu e (tyFollow tyUnit (tyRecv tyInt .:. TyVar e))), noA, noA) ] where t1 <:! t2 = Left (t1, t2) t1 >:! t2 = Right (t1, t2) Left (t1, t2) ==! (ta, tb, tc, td) = T.assertEqual (show t1 ++ " `subtype` " ++ show t2) (Right ([ta, tb, tc, td], [])) (runEither Left Right $ subtype 100 set t1 [] t2) Right (t1, t2) ==! (ta, tb, tc, td) = T.assertEqual (show t1 ++ " `supertype` " ++ show t2) (Right ([], [ta, tb, tc, td])) (runEither Left Right $ subtype 100 [] t2 set t1) t1 !<: t2 = T.assertEqual (show t1 ++ " `subtype` " ++ show t2 ++ " = ERROR") Nothing (subtype 100 set t1 [] t2) t1 !>: t2 = T.assertEqual (show t1 ++ " `supertype` " ++ show t2 ++ " = ERROR") Nothing (subtype 100 [] t2 set t1) infix 2 ==! infix 4 <:!, !<:, >:!, !>: noU = tyBot; noA = tyBot set = [a, b, c, d] a = tvUn "a"; b = tvUn "b"; c = tvAf "c"; d = tvAf "d" e = tvAf "e"; f = tvAf "f" tests :: IO () tests = do T.runTestTT subtypeTests T.runTestTT joinTests T.runTestTT uvarsTests return ()