{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TemplateHaskell #-} -- | -- Data types for types -- module Language.PureScript.Types where import Prelude.Compat import Protolude (ordNub) import Control.Arrow (first) import Control.Monad ((<=<)) import qualified Data.Aeson as A import qualified Data.Aeson.TH as A import Data.List (sortBy) import Data.Ord (comparing) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Data.Text (Text) import qualified Data.Text as T import Language.PureScript.AST.SourcePos import Language.PureScript.Kinds import Language.PureScript.Names import Language.PureScript.Label (Label) import Language.PureScript.PSString (PSString) -- | -- An identifier for the scope of a skolem variable -- newtype SkolemScope = SkolemScope { runSkolemScope :: Int } deriving (Show, Eq, Ord, A.ToJSON, A.FromJSON) -- | -- The type of types -- data Type -- | A unification variable of type Type = TUnknown Int -- | A named type variable | TypeVar Text -- | A type-level string | TypeLevelString PSString -- | A type wildcard, as would appear in a partial type synonym | TypeWildcard SourceSpan -- | A type constructor | TypeConstructor (Qualified (ProperName 'TypeName)) -- | A type operator. This will be desugared into a type constructor during the -- "operators" phase of desugaring. | TypeOp (Qualified (OpName 'TypeOpName)) -- | A type application | TypeApp Type Type -- | Forall quantifier | ForAll Text Type (Maybe SkolemScope) -- | A type with a set of type class constraints | ConstrainedType Constraint Type -- | A skolem constant | Skolem Text Int SkolemScope (Maybe SourceSpan) -- | An empty row | REmpty -- | A non-empty row | RCons Label Type Type -- | A type with a kind annotation | KindedType Type Kind -- | A placeholder used in pretty printing | PrettyPrintFunction Type Type -- | A placeholder used in pretty printing | PrettyPrintObject Type -- | A placeholder used in pretty printing | PrettyPrintForAll [Text] Type -- | Binary operator application. During the rebracketing phase of desugaring, -- this data constructor will be removed. | BinaryNoParensType Type Type Type -- | Explicit parentheses. During the rebracketing phase of desugaring, this -- data constructor will be removed. -- -- Note: although it seems this constructor is not used, it _is_ useful, -- since it prevents certain traversals from matching. | ParensInType Type deriving (Show, Eq, Ord) -- | Additional data relevant to type class constraints data ConstraintData = PartialConstraintData [[Text]] Bool -- ^ Data to accompany a Partial constraint generated by the exhaustivity checker. -- It contains (rendered) binder information for those binders which were -- not matched, and a flag indicating whether the list was truncated or not. -- Note: we use 'Text' here because using 'Binder' would introduce a cyclic -- dependency in the module graph. deriving (Show, Eq, Ord) -- | A typeclass constraint data Constraint = Constraint { constraintClass :: Qualified (ProperName 'ClassName) -- ^ constraint class name , constraintArgs :: [Type] -- ^ type arguments , constraintData :: Maybe ConstraintData -- ^ additional data relevant to this constraint } deriving (Show, Eq, Ord) mapConstraintArgs :: ([Type] -> [Type]) -> Constraint -> Constraint mapConstraintArgs f c = c { constraintArgs = f (constraintArgs c) } overConstraintArgs :: Functor f => ([Type] -> f [Type]) -> Constraint -> f Constraint overConstraintArgs f c = (\args -> c { constraintArgs = args }) <$> f (constraintArgs c) $(A.deriveJSON A.defaultOptions ''Type) $(A.deriveJSON A.defaultOptions ''Constraint) $(A.deriveJSON A.defaultOptions ''ConstraintData) -- | Convert a row to a list of pairs of labels and types rowToList :: Type -> ([(Label, Type)], Type) rowToList = go where go (RCons name ty row) = first ((name, ty) :) (rowToList row) go r = ([], r) -- | Convert a row to a list of pairs of labels and types, sorted by the labels. rowToSortedList :: Type -> ([(Label, Type)], Type) rowToSortedList = first (sortBy (comparing fst)) . rowToList -- | Convert a list of labels and types to a row rowFromList :: ([(Label, Type)], Type) -> Type rowFromList (xs, r) = foldr (uncurry RCons) r xs -- | Check whether a type is a monotype isMonoType :: Type -> Bool isMonoType ForAll{} = False isMonoType (ParensInType t) = isMonoType t isMonoType (KindedType t _) = isMonoType t isMonoType _ = True -- | Universally quantify a type mkForAll :: [Text] -> Type -> Type mkForAll args ty = foldl (\t arg -> ForAll arg t Nothing) ty args -- | Replace a type variable, taking into account variable shadowing replaceTypeVars :: Text -> Type -> Type -> Type replaceTypeVars v r = replaceAllTypeVars [(v, r)] -- | Replace named type variables with types replaceAllTypeVars :: [(Text, Type)] -> Type -> Type replaceAllTypeVars = go [] where go :: [Text] -> [(Text, Type)] -> Type -> Type go _ m (TypeVar v) = fromMaybe (TypeVar v) (v `lookup` m) go bs m (TypeApp t1 t2) = TypeApp (go bs m t1) (go bs m t2) go bs m f@(ForAll v t sco) | v `elem` keys = go bs (filter ((/= v) . fst) m) f | v `elem` usedVars = let v' = genName v (keys ++ bs ++ usedVars) t' = go bs [(v, TypeVar v')] t in ForAll v' (go (v' : bs) m t') sco | otherwise = ForAll v (go (v : bs) m t) sco where keys = map fst m usedVars = concatMap (usedTypeVariables . snd) m go bs m (ConstrainedType c t) = ConstrainedType (mapConstraintArgs (map (go bs m)) c) (go bs m t) go bs m (RCons name' t r) = RCons name' (go bs m t) (go bs m r) go bs m (KindedType t k) = KindedType (go bs m t) k go bs m (BinaryNoParensType t1 t2 t3) = BinaryNoParensType (go bs m t1) (go bs m t2) (go bs m t3) go bs m (ParensInType t) = ParensInType (go bs m t) go _ _ ty = ty genName orig inUse = try' 0 where try' :: Integer -> Text try' n | (orig <> T.pack (show n)) `elem` inUse = try' (n + 1) | otherwise = orig <> T.pack (show n) -- | Collect all type variables appearing in a type usedTypeVariables :: Type -> [Text] usedTypeVariables = ordNub . everythingOnTypes (++) go where go (TypeVar v) = [v] go _ = [] -- | Collect all free type variables appearing in a type freeTypeVariables :: Type -> [Text] freeTypeVariables = ordNub . go [] where go :: [Text] -> Type -> [Text] go bound (TypeVar v) | v `notElem` bound = [v] go bound (TypeApp t1 t2) = go bound t1 ++ go bound t2 go bound (ForAll v t _) = go (v : bound) t go bound (ConstrainedType c t) = concatMap (go bound) (constraintArgs c) ++ go bound t go bound (RCons _ t r) = go bound t ++ go bound r go bound (KindedType t _) = go bound t go bound (BinaryNoParensType t1 t2 t3) = go bound t1 ++ go bound t2 ++ go bound t3 go bound (ParensInType t) = go bound t go _ _ = [] -- | Universally quantify over all type variables appearing free in a type quantify :: Type -> Type quantify ty = foldr (\arg t -> ForAll arg t Nothing) ty $ freeTypeVariables ty -- | Move all universal quantifiers to the front of a type moveQuantifiersToFront :: Type -> Type moveQuantifiersToFront = go [] [] where go qs cs (ForAll q ty sco) = go ((q, sco) : qs) cs ty go qs cs (ConstrainedType c ty) = go qs (c : cs) ty go qs cs ty = foldl (\ty' (q, sco) -> ForAll q ty' sco) (foldl (flip ConstrainedType) ty cs) qs -- | Check if a type contains wildcards containsWildcards :: Type -> Bool containsWildcards = everythingOnTypes (||) go where go :: Type -> Bool go TypeWildcard{} = True go _ = False everywhereOnTypes :: (Type -> Type) -> Type -> Type everywhereOnTypes f = go where go (TypeApp t1 t2) = f (TypeApp (go t1) (go t2)) go (ForAll arg ty sco) = f (ForAll arg (go ty) sco) go (ConstrainedType c ty) = f (ConstrainedType (mapConstraintArgs (map go) c) (go ty)) go (RCons name ty rest) = f (RCons name (go ty) (go rest)) go (KindedType ty k) = f (KindedType (go ty) k) go (PrettyPrintFunction t1 t2) = f (PrettyPrintFunction (go t1) (go t2)) go (PrettyPrintObject t) = f (PrettyPrintObject (go t)) go (PrettyPrintForAll args t) = f (PrettyPrintForAll args (go t)) go (BinaryNoParensType t1 t2 t3) = f (BinaryNoParensType (go t1) (go t2) (go t3)) go (ParensInType t) = f (ParensInType (go t)) go other = f other everywhereOnTypesTopDown :: (Type -> Type) -> Type -> Type everywhereOnTypesTopDown f = go . f where go (TypeApp t1 t2) = TypeApp (go (f t1)) (go (f t2)) go (ForAll arg ty sco) = ForAll arg (go (f ty)) sco go (ConstrainedType c ty) = ConstrainedType (mapConstraintArgs (map (go . f)) c) (go (f ty)) go (RCons name ty rest) = RCons name (go (f ty)) (go (f rest)) go (KindedType ty k) = KindedType (go (f ty)) k go (PrettyPrintFunction t1 t2) = PrettyPrintFunction (go (f t1)) (go (f t2)) go (PrettyPrintObject t) = PrettyPrintObject (go (f t)) go (PrettyPrintForAll args t) = PrettyPrintForAll args (go (f t)) go (BinaryNoParensType t1 t2 t3) = BinaryNoParensType (go (f t1)) (go (f t2)) (go (f t3)) go (ParensInType t) = ParensInType (go (f t)) go other = f other everywhereOnTypesM :: Monad m => (Type -> m Type) -> Type -> m Type everywhereOnTypesM f = go where go (TypeApp t1 t2) = (TypeApp <$> go t1 <*> go t2) >>= f go (ForAll arg ty sco) = (ForAll arg <$> go ty <*> pure sco) >>= f go (ConstrainedType c ty) = (ConstrainedType <$> overConstraintArgs (mapM go) c <*> go ty) >>= f go (RCons name ty rest) = (RCons name <$> go ty <*> go rest) >>= f go (KindedType ty k) = (KindedType <$> go ty <*> pure k) >>= f go (PrettyPrintFunction t1 t2) = (PrettyPrintFunction <$> go t1 <*> go t2) >>= f go (PrettyPrintObject t) = (PrettyPrintObject <$> go t) >>= f go (PrettyPrintForAll args t) = (PrettyPrintForAll args <$> go t) >>= f go (BinaryNoParensType t1 t2 t3) = (BinaryNoParensType <$> go t1 <*> go t2 <*> go t3) >>= f go (ParensInType t) = (ParensInType <$> go t) >>= f go other = f other everywhereOnTypesTopDownM :: Monad m => (Type -> m Type) -> Type -> m Type everywhereOnTypesTopDownM f = go <=< f where go (TypeApp t1 t2) = TypeApp <$> (f t1 >>= go) <*> (f t2 >>= go) go (ForAll arg ty sco) = ForAll arg <$> (f ty >>= go) <*> pure sco go (ConstrainedType c ty) = ConstrainedType <$> overConstraintArgs (mapM (go <=< f)) c <*> (f ty >>= go) go (RCons name ty rest) = RCons name <$> (f ty >>= go) <*> (f rest >>= go) go (KindedType ty k) = KindedType <$> (f ty >>= go) <*> pure k go (PrettyPrintFunction t1 t2) = PrettyPrintFunction <$> (f t1 >>= go) <*> (f t2 >>= go) go (PrettyPrintObject t) = PrettyPrintObject <$> (f t >>= go) go (PrettyPrintForAll args t) = PrettyPrintForAll args <$> (f t >>= go) go (BinaryNoParensType t1 t2 t3) = BinaryNoParensType <$> (f t1 >>= go) <*> (f t2 >>= go) <*> (f t3 >>= go) go (ParensInType t) = ParensInType <$> (f t >>= go) go other = f other everythingOnTypes :: (r -> r -> r) -> (Type -> r) -> Type -> r everythingOnTypes (<+>) f = go where go t@(TypeApp t1 t2) = f t <+> go t1 <+> go t2 go t@(ForAll _ ty _) = f t <+> go ty go t@(ConstrainedType c ty) = foldl (<+>) (f t) (map go (constraintArgs c)) <+> go ty go t@(RCons _ ty rest) = f t <+> go ty <+> go rest go t@(KindedType ty _) = f t <+> go ty go t@(PrettyPrintFunction t1 t2) = f t <+> go t1 <+> go t2 go t@(PrettyPrintObject t1) = f t <+> go t1 go t@(PrettyPrintForAll _ t1) = f t <+> go t1 go t@(BinaryNoParensType t1 t2 t3) = f t <+> go t1 <+> go t2 <+> go t3 go t@(ParensInType t1) = f t <+> go t1 go other = f other everythingWithContextOnTypes :: s -> r -> (r -> r -> r) -> (s -> Type -> (s, r)) -> Type -> r everythingWithContextOnTypes s0 r0 (<+>) f = go' s0 where go' s t = let (s', r) = f s t in r <+> go s' t go s (TypeApp t1 t2) = go' s t1 <+> go' s t2 go s (ForAll _ ty _) = go' s ty go s (ConstrainedType c ty) = foldl (<+>) r0 (map (go' s) (constraintArgs c)) <+> go' s ty go s (RCons _ ty rest) = go' s ty <+> go' s rest go s (KindedType ty _) = go' s ty go s (PrettyPrintFunction t1 t2) = go' s t1 <+> go' s t2 go s (PrettyPrintObject t1) = go' s t1 go s (PrettyPrintForAll _ t1) = go' s t1 go s (BinaryNoParensType t1 t2 t3) = go' s t1 <+> go' s t2 <+> go' s t3 go s (ParensInType t1) = go' s t1 go _ _ = r0