{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
module Language.PureScript.TypeChecker.Subsumption
( subsumes
) where
import Prelude.Compat
import Control.Monad (when)
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.State.Class (MonadState(..))
import Data.Foldable (for_)
import Data.List (uncons)
import Data.List.Ordered (minusBy')
import Data.Ord (comparing)
import Language.PureScript.AST
import Language.PureScript.Crash
import Language.PureScript.Environment
import Language.PureScript.Errors
import Language.PureScript.TypeChecker.Monad
import Language.PureScript.TypeChecker.Skolems
import Language.PureScript.TypeChecker.Unify
import Language.PureScript.Types
data Mode = Elaborate | NoElaborate
data ModeSing (mode :: Mode) where
SElaborate :: ModeSing 'Elaborate
SNoElaborate :: ModeSing 'NoElaborate
type family Coercion (mode :: Mode) where
Coercion 'Elaborate = Expr -> Expr
Coercion 'NoElaborate = ()
defaultCoercion :: ModeSing mode -> Coercion mode
defaultCoercion SElaborate = id
defaultCoercion SNoElaborate = ()
subsumes
:: (MonadError MultipleErrors m, MonadState CheckState m)
=> SourceType
-> SourceType
-> m (Expr -> Expr)
subsumes ty1 ty2 =
withErrorMessageHint (ErrorInSubsumption ty1 ty2) $
subsumes' SElaborate ty1 ty2
subsumes'
:: (MonadError MultipleErrors m, MonadState CheckState m)
=> ModeSing mode
-> SourceType
-> SourceType
-> m (Coercion mode)
subsumes' mode (ForAll _ ident _ ty1 _) ty2 = do
replaced <- replaceVarWithUnknown ident ty1
subsumes' mode replaced ty2
subsumes' mode ty1 (ForAll _ ident _ ty2 sco) =
case sco of
Just sco' -> do
sko <- newSkolemConstant
let sk = skolemize NullSourceAnn ident sko sco' ty2
subsumes' mode ty1 sk
Nothing -> internalError "subsumes: unspecified skolem scope"
subsumes' mode (TypeApp _ (TypeApp _ f1 arg1) ret1) (TypeApp _ (TypeApp _ f2 arg2) ret2) | eqType f1 tyFunction && eqType f2 tyFunction = do
subsumes' SNoElaborate arg2 arg1
subsumes' SNoElaborate ret1 ret2
return (defaultCoercion mode)
subsumes' mode (KindedType _ ty1 _) ty2 =
subsumes' mode ty1 ty2
subsumes' mode ty1 (KindedType _ ty2 _) =
subsumes' mode ty1 ty2
subsumes' SElaborate (ConstrainedType _ con ty1) ty2 = do
dicts <- getTypeClassDictionaries
hints <- getHints
elaborate <- subsumes' SElaborate ty1 ty2
let addDicts val = App val (TypeClassDictionary con dicts hints)
return (elaborate . addDicts)
subsumes' mode (TypeApp _ f1 r1) (TypeApp _ f2 r2) | eqType f1 tyRecord && eqType f2 tyRecord = do
let (common, ((ts1', r1'), (ts2', r2'))) = alignRowsWith (subsumes' SNoElaborate) r1 r2
when (eqType r1' $ REmpty ())
(for_ (firstMissingProp ts2' ts1') (throwError . errorMessage . PropertyIsMissing . rowListLabel))
when (eqType r2' $ REmpty ())
(for_ (firstMissingProp ts1' ts2') (throwError . errorMessage . AdditionalProperty . rowListLabel))
sequence_ common
unifyTypes (rowFromList (ts1', r1')) (rowFromList (ts2', r2'))
return (defaultCoercion mode)
where
firstMissingProp t1 t2 = fst <$> uncons (minusBy' (comparing rowListLabel) t1 t2)
subsumes' mode ty1 ty2@(TypeApp _ obj _) | obj == tyRecord =
subsumes' mode ty2 ty1
subsumes' mode ty1 ty2 = do
unifyTypes ty1 ty2
return (defaultCoercion mode)