hydra-kernel-0.16.0: src/main/haskell/Hydra/Checking.hs
-- Note: this is an automatically generated file. Do not edit.
-- | Type checking and type reconstruction (type-of) for the results of Hydra unification and inference
module Hydra.Checking where
import qualified Hydra.Ast as Ast
import qualified Hydra.Coders as Coders
import qualified Hydra.Constants as Constants
import qualified Hydra.Core as Core
import qualified Hydra.Dependencies as Dependencies
import qualified Hydra.Error.Checking as Checking
import qualified Hydra.Error.Core as ErrorCore
import qualified Hydra.Error.Packaging as ErrorPackaging
import qualified Hydra.Errors as Errors
import qualified Hydra.Extract.Core as ExtractCore
import qualified Hydra.Formatting as Formatting
import qualified Hydra.Graph as Graph
import qualified Hydra.Json.Model as Model
import qualified Hydra.Lexical as Lexical
import qualified Hydra.Haskell.Lib.Eithers as Eithers
import qualified Hydra.Haskell.Lib.Equality as Equality
import qualified Hydra.Haskell.Lib.Lists as Lists
import qualified Hydra.Haskell.Lib.Literals as Literals
import qualified Hydra.Haskell.Lib.Logic as Logic
import qualified Hydra.Haskell.Lib.Maps as Maps
import qualified Hydra.Haskell.Lib.Optionals as Optionals
import qualified Hydra.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Haskell.Lib.Sets as Sets
import qualified Hydra.Haskell.Lib.Strings as Strings
import qualified Hydra.Names as Names
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Parsing as Parsing
import qualified Hydra.Paths as Paths
import qualified Hydra.Query as Query
import qualified Hydra.Reflect as Reflect
import qualified Hydra.Relational as Relational
import qualified Hydra.Resolution as Resolution
import qualified Hydra.Rewriting as Rewriting
import qualified Hydra.Scoping as Scoping
import qualified Hydra.Show.Core as ShowCore
import qualified Hydra.Show.Errors as ShowErrors
import qualified Hydra.Show.Variants as ShowVariants
import qualified Hydra.Strip as Strip
import qualified Hydra.Substitution as Substitution
import qualified Hydra.Tabular as Tabular
import qualified Hydra.Testing as Testing
import qualified Hydra.Topology as Topology
import qualified Hydra.Typed as Typed
import qualified Hydra.Typing as Typing
import qualified Hydra.Util as Util
import qualified Hydra.Validation as Validation
import qualified Hydra.Variables as Variables
import qualified Hydra.Variants as Variants
import Prelude hiding (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci
import qualified Data.Map as M
import qualified Data.Set as S
-- | True if every element of the list is equal to every other element (vacuously true for the empty list)
allEqual :: Eq t0 => ([t0] -> Bool)
allEqual els =
Optionals.cases (Lists.uncons els) True (\uc ->
let h = Pairs.first uc
t = Pairs.second uc
in (Lists.foldl (\b -> \x -> Logic.and b (Equality.equal x h)) True t))
-- | Apply type arguments to a type, substituting forall-bound variables
applyTypeArgumentsToType :: t0 -> Graph.Graph -> [Core.Type] -> Core.Type -> Either Errors.Error Core.Type
applyTypeArgumentsToType cx tx typeArgs t =
Optionals.cases (Lists.uncons typeArgs) (Right t) (\uc ->
let ah = Pairs.first uc
at = Pairs.second uc
in case t of
Core.TypeForall v0 ->
let v = Core.forallTypeParameter v0
tbody = Core.forallTypeBody v0
in (applyTypeArgumentsToType cx tx at (Substitution.substInType (Typing.TypeSubst (Maps.singleton v ah)) tbody))
_ -> Left (Errors.ErrorExtraction (Errors.ExtractionErrorUnexpectedShape (Errors.UnexpectedShapeError {
Errors.unexpectedShapeErrorExpected = "forall type",
Errors.unexpectedShapeErrorActual = (Strings.cat [
ShowCore.type_ t,
". Trying to apply ",
(Literals.showInt32 (Lists.length typeArgs)),
" type args: ",
(Formatting.showList ShowCore.type_ typeArgs),
". Context has vars: {",
(Strings.intercalate ", " (Lists.map Core.unName (Maps.keys (Graph.graphBoundTypes tx)))),
"}"])}))))
-- | Check that a term has no unbound type variables (Either version)
checkForUnboundTypeVariables :: t0 -> Graph.Graph -> Core.Term -> Either Errors.Error ()
checkForUnboundTypeVariables cx tx term0 =
let svars = Sets.fromList (Maps.keys (Graph.graphSchemaTypes tx))
checkRecursive =
\vars -> \trace -> \lbinding -> \term ->
let recurse = checkRecursive vars trace lbinding
dflt = Eithers.bind (Eithers.mapList recurse (Rewriting.subterms term)) (\_ -> Right ())
check =
\typ ->
let freevars = Variables.freeVariablesInType typ
badvars = Sets.difference (Sets.difference freevars vars) svars
in (Logic.ifElse (Sets.null badvars) (Right ()) (Left (Errors.ErrorChecking (Checking.CheckingErrorUnboundTypeVariables (Checking.UnboundTypeVariablesError {
Checking.unboundTypeVariablesErrorVariables = badvars,
Checking.unboundTypeVariablesErrorType = typ})))))
checkOptional = \m -> Eithers.bind (Eithers.mapOptional check m) (\_ -> Right ())
in case term of
Core.TermLambda v0 -> Eithers.bind (checkOptional (Core.lambdaDomain v0)) (\_ -> recurse (Core.lambdaBody v0))
Core.TermLet v0 ->
let forBinding =
\b ->
let bterm = Core.bindingTerm b
newVars =
Optionals.cases (Core.bindingTypeScheme b) vars (\ts -> Sets.union vars (Sets.fromList (Core.typeSchemeVariables ts)))
newTrace = Lists.cons (Core.unName (Core.bindingName b)) trace
in (checkRecursive newVars newTrace (Just b) bterm)
in (Eithers.bind (Eithers.mapList forBinding (Core.letBindings v0)) (\_ -> recurse (Core.letBody v0)))
Core.TermTypeApplication v0 -> Eithers.bind (check (Core.typeApplicationTermType v0)) (\_ -> recurse (Core.typeApplicationTermBody v0))
Core.TermTypeLambda v0 -> Eithers.bind (check (Core.TypeVariable (Core.typeLambdaParameter v0))) (\_ -> recurse (Core.typeLambdaBody v0))
_ -> dflt
in (checkRecursive Sets.empty [
"top level"] Nothing term0)
-- | Check that a nominal type is applied to the correct number of type arguments (Either version)
checkNominalApplication :: Typing.InferenceContext -> Graph.Graph -> Core.Name -> [Core.Type] -> Either Errors.Error ((), Typing.InferenceContext)
checkNominalApplication cx tx tname typeArgs =
Eithers.bind (Resolution.requireSchemaType cx (Graph.graphSchemaTypes tx) tname) (\result ->
let schemaType = Pairs.first result
cx2 = Pairs.second result
vars = Core.typeSchemeVariables schemaType
varslen = Lists.length vars
argslen = Lists.length typeArgs
in (Logic.ifElse (Equality.equal varslen argslen) (Right ((), cx2)) (Left (Errors.ErrorChecking (Checking.CheckingErrorTypeArityMismatch (Checking.TypeArityMismatchError {
Checking.typeArityMismatchErrorType = (Core.TypeVariable tname),
Checking.typeArityMismatchErrorExpectedArity = varslen,
Checking.typeArityMismatchErrorActualArity = argslen,
Checking.typeArityMismatchErrorTypeArguments = typeArgs}))))))
-- | Ensure all types in a list are equal and return the common type
checkSameType :: t0 -> Graph.Graph -> String -> [Core.Type] -> Either Errors.Error Core.Type
checkSameType cx tx desc types =
let unequalErr =
Left (Errors.ErrorChecking (Checking.CheckingErrorUnequalTypes (Checking.UnequalTypesError {
Checking.unequalTypesErrorTypes = types,
Checking.unequalTypesErrorDescription = desc})))
in (Logic.ifElse (typesAllEffectivelyEqual tx types) (Optionals.cases (Lists.maybeHead types) unequalErr (\t -> Right t)) unequalErr)
-- | Check that a term has the expected type
checkType :: Typing.InferenceContext -> Graph.Graph -> Core.Term -> Core.Type -> Either Errors.Error ()
checkType cx tx term typ =
let vars = Graph.graphTypeVariables tx
in (Logic.ifElse Constants.debugInference (Eithers.bind (Eithers.map (\_p -> Pairs.first _p) (typeOf cx tx [] term)) (\t0 -> Logic.ifElse (typesEffectivelyEqual tx t0 typ) (Right ()) (Left (Errors.ErrorChecking (Checking.CheckingErrorTypeMismatch (Checking.TypeMismatchError {
Checking.typeMismatchErrorExpectedType = typ,
Checking.typeMismatchErrorActualType = t0})))))) (Right ()))
-- | Sanity-check a type substitution arising from unification. Specifically, check that schema types have not been inappropriately unified with type variables inferred from terms.
checkTypeSubst :: t0 -> Graph.Graph -> Typing.TypeSubst -> Either Errors.Error Typing.TypeSubst
checkTypeSubst cx tx subst =
let s = Typing.unTypeSubst subst
vars = Sets.fromList (Maps.keys s)
suspectVars = Sets.intersection vars (Sets.fromList (Maps.keys (Graph.graphSchemaTypes tx)))
isNominal =
\ts -> case (Strip.deannotateType (Core.typeSchemeBody ts)) of
Core.TypeRecord _ -> True
Core.TypeUnion _ -> True
Core.TypeWrap _ -> True
_ -> False
badVars =
Sets.fromList (Lists.filter (\v -> Optionals.cases (Lexical.dereferenceSchemaType v (Graph.graphSchemaTypes tx)) False isNominal) (Sets.toList suspectVars))
badPairs = Lists.filter (\p -> Sets.member (Pairs.first p) badVars) (Maps.toList s)
printPair = \p -> Strings.cat2 (Strings.cat2 (Core.unName (Pairs.first p)) " --> ") (ShowCore.type_ (Pairs.second p))
in (Logic.ifElse (Sets.null badVars) (Right subst) (Left (Errors.ErrorChecking (Checking.CheckingErrorIncorrectUnification (Checking.IncorrectUnificationError {
Checking.incorrectUnificationErrorSubstitution = subst})))))
-- | Check that all type variables in a type are bound. NOTE: This check is currently disabled to allow phantom type variables from polymorphic instantiation to pass through. The proper fix is to ensure `typeOf` doesn't create fresh variables for post-inference code.
checkTypeVariables :: t0 -> t1 -> ()
checkTypeVariables _tx _typ = ()
-- | Check if a type contains any type variable from the current scope
containsInScopeTypeVars :: Graph.Graph -> Core.Type -> Bool
containsInScopeTypeVars tx t =
let vars = Graph.graphTypeVariables tx
freeVars = Variables.freeVariablesInTypeSimple t
in (Logic.not (Sets.null (Sets.intersection vars freeVars)))
-- | Normalize free type variables in a type to canonical names based on order of first occurrence. This allows comparing types that differ only in the naming of free type variables.
normalizeTypeFreeVars :: Core.Type -> Core.Type
normalizeTypeFreeVars typ =
let collectVars =
\acc -> \t -> case t of
Core.TypeVariable v0 -> Logic.ifElse (Maps.member v0 acc) acc (Maps.insert v0 (Core.Name (Strings.cat2 "_tv" (Literals.showInt32 (Maps.size acc)))) acc)
_ -> acc
subst = Rewriting.foldOverType Coders.TraversalOrderPre collectVars Maps.empty typ
in (Variables.substituteTypeVariables subst typ)
-- | Get the bound types from a graph as a type environment
toFContext :: Graph.Graph -> M.Map Core.Name Core.Type
toFContext cx = Maps.map Scoping.typeSchemeToFType (Graph.graphBoundTypes cx)
-- | Check whether two lists of types are effectively equal, disregarding type aliases
typeListsEffectivelyEqual :: Graph.Graph -> [Core.Type] -> [Core.Type] -> Bool
typeListsEffectivelyEqual tx tlist1 tlist2 =
Logic.ifElse (Equality.equal (Lists.length tlist1) (Lists.length tlist2)) (Lists.foldl Logic.and True (Lists.zipWith (typesEffectivelyEqual tx) tlist1 tlist2)) False
-- | Given a type context, reconstruct the type of a System F term
typeOf :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Term -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOf cx tx typeArgs term =
let cx1 = cx
in case term of
Core.TermAnnotated v0 -> typeOfAnnotatedTerm cx1 tx typeArgs v0
Core.TermApplication v0 -> typeOfApplication cx1 tx typeArgs v0
Core.TermCases v0 -> typeOfCaseStatement cx1 tx typeArgs v0
Core.TermEither v0 -> typeOfEither cx1 tx typeArgs v0
Core.TermLambda v0 -> typeOfLambda cx1 tx typeArgs v0
Core.TermLet v0 -> typeOfLet cx1 tx typeArgs v0
Core.TermList v0 -> typeOfList cx1 tx typeArgs v0
Core.TermLiteral v0 -> typeOfLiteral cx1 tx typeArgs v0
Core.TermMap v0 -> typeOfMap cx1 tx typeArgs v0
Core.TermOptional v0 -> typeOfMaybe cx1 tx typeArgs v0
Core.TermPair v0 -> typeOfPair cx1 tx typeArgs v0
Core.TermProject v0 -> typeOfProjection cx1 tx typeArgs v0
Core.TermRecord v0 -> typeOfRecord cx1 tx typeArgs v0
Core.TermSet v0 -> typeOfSet cx1 tx typeArgs v0
Core.TermTypeApplication v0 -> typeOfTypeApplication cx1 tx typeArgs v0
Core.TermTypeLambda v0 -> typeOfTypeLambda cx1 tx typeArgs v0
Core.TermInject v0 -> typeOfInjection cx1 tx typeArgs v0
Core.TermUnit -> typeOfUnit cx1 tx typeArgs
Core.TermUnwrap v0 -> typeOfUnwrap cx1 tx typeArgs v0
Core.TermVariable v0 -> typeOfVariable cx1 tx typeArgs v0
Core.TermWrap v0 -> typeOfWrappedTerm cx1 tx typeArgs v0
_ -> Left (Errors.ErrorChecking (Checking.CheckingErrorUnsupportedTermVariant (Checking.UnsupportedTermVariantError {
Checking.unsupportedTermVariantErrorTermVariant = (Reflect.termVariant term)})))
-- | Reconstruct the type of an annotated term (Either/InferenceContext version)
typeOfAnnotatedTerm :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.AnnotatedTerm -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfAnnotatedTerm cx tx typeArgs at = typeOf cx tx typeArgs (Core.annotatedTermBody at)
-- | Reconstruct the type of an application term (Either/InferenceContext version)
typeOfApplication :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Application -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfApplication cx tx typeArgs app =
let fun = Core.applicationFunction app
arg = Core.applicationArgument app
tryType =
\cx0 -> \tfun -> \targ -> case tfun of
Core.TypeForall v0 -> tryType cx0 (Core.forallTypeBody v0) targ
Core.TypeFunction v0 ->
let dom = Core.functionTypeDomain v0
cod = Core.functionTypeCodomain v0
in (Logic.ifElse (typesEffectivelyEqual tx dom targ) (Right (cod, cx0)) (Left (Errors.ErrorChecking (Checking.CheckingErrorTypeMismatch (Checking.TypeMismatchError {
Checking.typeMismatchErrorExpectedType = dom,
Checking.typeMismatchErrorActualType = targ})))))
Core.TypeVariable _ ->
let nameResult = Names.freshName cx0
freshN = Pairs.first nameResult
cx1 = Pairs.second nameResult
in (Right (Core.TypeVariable freshN, cx1))
_ -> Left (Errors.ErrorChecking (Checking.CheckingErrorNotAFunctionType (Checking.NotAFunctionTypeError {
Checking.notAFunctionTypeErrorType = tfun})))
in (Eithers.bind (typeOf cx tx [] fun) (\result1 ->
let tfun = Pairs.first result1
cx2 = Pairs.second result1
in (Eithers.bind (typeOf cx2 tx [] arg) (\result2 ->
let targ = Pairs.first result2
cx3 = Pairs.second result2
in (Eithers.bind (tryType cx3 tfun targ) (\result3 ->
let t = Pairs.first result3
cx4 = Pairs.second result3
in (Eithers.bind (applyTypeArgumentsToType cx4 tx typeArgs t) (\applied -> Right (applied, cx4)))))))))
-- | Reconstruct the type of a case statement (Either/InferenceContext version)
typeOfCaseStatement :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.CaseStatement -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfCaseStatement cx tx typeArgs cs =
let tname = Core.caseStatementTypeName cs
dflt = Core.caseStatementDefault cs
cases = Core.caseStatementCases cs
cterms = Lists.map Core.caseAlternativeHandler cases
in (Eithers.bind (Eithers.mapOptional (\e -> typeOf cx tx [] e) dflt) (\dfltResult ->
let tdflt = Optionals.map Pairs.first dfltResult
cx2 = Optionals.cases dfltResult cx Pairs.second
foldResult =
Lists.foldl (\acc -> \term -> Eithers.bind acc (\accR ->
let types = Pairs.first accR
cxA = Pairs.second accR
in (Eithers.bind (typeOf cxA tx [] term) (\tResult ->
let t = Pairs.first tResult
cxB = Pairs.second tResult
in (Right (Lists.concat2 types (Lists.pure t), cxB)))))) (Right ([], cx2)) cterms
in (Eithers.bind foldResult (\foldR ->
let tcterms = Pairs.first foldR
cx3 = Pairs.second foldR
fcodsResult =
Lists.foldl (\acc -> \t -> Eithers.bind acc (\accR ->
let cods = Pairs.first accR
in (Eithers.bind (ExtractCore.functionType t) (\ft -> Right (Lists.concat2 cods (Lists.pure (Core.functionTypeCodomain ft)), cx3))))) (Right ([], cx3)) tcterms
in (Eithers.bind fcodsResult (\fcodsR ->
let fcods = Pairs.first fcodsR
cods = Optionals.cat (Lists.cons tdflt (Lists.map Optionals.pure fcods))
in (Eithers.bind (checkSameType cx3 tx "case branches" cods) (\cod -> Right (
Core.TypeFunction (Core.FunctionType {
Core.functionTypeDomain = (Resolution.nominalApplication tname typeArgs),
Core.functionTypeCodomain = cod}),
cx3)))))))))
-- | Reconstruct the type of an either value (Either/InferenceContext version)
typeOfEither :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Either Core.Term Core.Term -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfEither cx tx typeArgs et =
let n = Lists.length typeArgs
arityErr =
Left (Errors.ErrorChecking (Checking.CheckingErrorTypeArityMismatch (Checking.TypeArityMismatchError {
Checking.typeArityMismatchErrorType = (Core.TypeEither (Core.EitherType {
Core.eitherTypeLeft = Core.TypeUnit,
Core.eitherTypeRight = Core.TypeUnit})),
Checking.typeArityMismatchErrorExpectedArity = 2,
Checking.typeArityMismatchErrorActualArity = n,
Checking.typeArityMismatchErrorTypeArguments = typeArgs})))
in (Optionals.cases (Lists.uncons typeArgs) arityErr (\uc0 ->
let ta0 = Pairs.first uc0
in (Optionals.cases (Lists.uncons (Pairs.second uc0)) arityErr (\uc1 ->
let ta1 = Pairs.first uc1
in (Logic.ifElse (Equality.equal n 2) (Eithers.either (\leftTerm -> Eithers.bind (typeOf cx tx [] leftTerm) (\result ->
let leftType = Pairs.first result
cx2 = Pairs.second result
in (Right (
Core.TypeEither (Core.EitherType {
Core.eitherTypeLeft = leftType,
Core.eitherTypeRight = ta1}),
cx2)))) (\rightTerm -> Eithers.bind (typeOf cx tx [] rightTerm) (\result ->
let rightType = Pairs.first result
cx2 = Pairs.second result
in (Right (
Core.TypeEither (Core.EitherType {
Core.eitherTypeLeft = ta0,
Core.eitherTypeRight = rightType}),
cx2)))) et) arityErr)))))
-- | Reconstruct the type of a union injection (Either/InferenceContext version)
typeOfInjection :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Injection -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfInjection cx tx typeArgs injection =
let tname = Core.injectionTypeName injection
field = Core.injectionField injection
fname = Core.fieldName field
fterm = Core.fieldTerm field
in (Eithers.bind (Resolution.requireSchemaType cx (Graph.graphSchemaTypes tx) tname) (\schemaResult ->
let schemaType = Pairs.first schemaResult
cx2 = Pairs.second schemaResult
svars = Core.typeSchemeVariables schemaType
sbody = Core.typeSchemeBody schemaType
in (Eithers.bind (ExtractCore.unionType tname sbody) (\sfields -> Eithers.bind (Resolution.findFieldType cx2 fname sfields) (\ftyp -> Right (Resolution.nominalApplication tname typeArgs, cx2))))))
-- | Reconstruct the type of a lambda function (Either/InferenceContext version)
typeOfLambda :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Lambda -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfLambda cx tx typeArgs l =
let v = Core.lambdaParameter l
mdom = Core.lambdaDomain l
body = Core.lambdaBody l
in (Eithers.bind (Optionals.cases mdom (Left (Errors.ErrorChecking (Checking.CheckingErrorUntypedLambda (Checking.UntypedLambdaError {
})))) (\dom ->
let types2 = Maps.insert v (Scoping.fTypeToTypeScheme dom) (Graph.graphBoundTypes tx)
in (Eithers.bind (typeOf cx (Graph.Graph {
Graph.graphBoundTerms = (Graph.graphBoundTerms tx),
Graph.graphBoundTypes = types2,
Graph.graphClassConstraints = (Graph.graphClassConstraints tx),
Graph.graphLambdaVariables = (Graph.graphLambdaVariables tx),
Graph.graphMetadata = (Graph.graphMetadata tx),
Graph.graphPrimitives = (Graph.graphPrimitives tx),
Graph.graphSchemaTypes = (Graph.graphSchemaTypes tx),
Graph.graphTypeVariables = (Graph.graphTypeVariables tx)}) [] body) (\codResult ->
let cod = Pairs.first codResult
cx2 = Pairs.second codResult
in (Right (
Core.TypeFunction (Core.FunctionType {
Core.functionTypeDomain = dom,
Core.functionTypeCodomain = cod}),
cx2)))))) (\tbodyResult ->
let tbody = Pairs.first tbodyResult
cx3 = Pairs.second tbodyResult
in (Eithers.bind (applyTypeArgumentsToType cx3 tx typeArgs tbody) (\applied -> Right (applied, cx3)))))
-- | Reconstruct the type of a let binding (Either/InferenceContext version)
typeOfLet :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Let -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfLet cx tx typeArgs letTerm =
let bs = Core.letBindings letTerm
body = Core.letBody letTerm
bnames = Lists.map Core.bindingName bs
bindingType =
\b -> Optionals.cases (Core.bindingTypeScheme b) (Left (Errors.ErrorChecking (Checking.CheckingErrorUntypedLetBinding (Checking.UntypedLetBindingError {
Checking.untypedLetBindingErrorBinding = b})))) (\ts -> Right (Scoping.typeSchemeToFType ts))
btypesResult =
Lists.foldl (\acc -> \b -> Eithers.bind acc (\accR ->
let types = Pairs.first accR
in (Eithers.bind (bindingType b) (\btype -> Right (Lists.concat2 types (Lists.pure btype), ()))))) (Right ([], ())) bs
in (Eithers.bind btypesResult (\btypesR ->
let btypes = Pairs.first btypesR
tx2 =
Graph.Graph {
Graph.graphBoundTerms = (Graph.graphBoundTerms tx),
Graph.graphBoundTypes = (Maps.union (Maps.fromList (Lists.zip bnames (Lists.map Scoping.fTypeToTypeScheme btypes))) (Graph.graphBoundTypes tx)),
Graph.graphClassConstraints = (Graph.graphClassConstraints tx),
Graph.graphLambdaVariables = (Graph.graphLambdaVariables tx),
Graph.graphMetadata = (Graph.graphMetadata tx),
Graph.graphPrimitives = (Graph.graphPrimitives tx),
Graph.graphSchemaTypes = (Graph.graphSchemaTypes tx),
Graph.graphTypeVariables = (Graph.graphTypeVariables tx)}
in (Eithers.bind (typeOf cx tx2 [] body) (\tResult ->
let t = Pairs.first tResult
cx2 = Pairs.second tResult
in (Eithers.bind (applyTypeArgumentsToType cx2 tx typeArgs t) (\applied -> Right (applied, cx2)))))))
-- | Reconstruct the type of a list (Either/InferenceContext version)
typeOfList :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> [Core.Term] -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfList cx tx typeArgs els =
let listArityErr =
Left (Errors.ErrorChecking (Checking.CheckingErrorTypeArityMismatch (Checking.TypeArityMismatchError {
Checking.typeArityMismatchErrorType = (Core.TypeList Core.TypeUnit),
Checking.typeArityMismatchErrorExpectedArity = 1,
Checking.typeArityMismatchErrorActualArity = (Lists.length typeArgs),
Checking.typeArityMismatchErrorTypeArguments = typeArgs})))
in (Logic.ifElse (Lists.null els) (Logic.ifElse (Equality.equal (Lists.length typeArgs) 1) (Optionals.cases (Lists.maybeHead typeArgs) listArityErr (\ta0 -> Right (Core.TypeList ta0, cx))) listArityErr) (
let foldResult =
Lists.foldl (\acc -> \term -> Eithers.bind acc (\accR ->
let types = Pairs.first accR
cxA = Pairs.second accR
in (Eithers.bind (typeOf cxA tx [] term) (\tResult ->
let t = Pairs.first tResult
cxB = Pairs.second tResult
in (Right (Lists.concat2 types (Lists.pure t), cxB)))))) (Right ([], cx)) els
in (Eithers.bind foldResult (\foldR ->
let eltypes = Pairs.first foldR
cx2 = Pairs.second foldR
in (Eithers.bind (checkSameType cx2 tx "list elements" eltypes) (\unifiedType -> Right (Core.TypeList unifiedType, cx2)))))))
-- | Reconstruct the type of a literal (Either/InferenceContext version)
typeOfLiteral :: t0 -> Graph.Graph -> [Core.Type] -> Core.Literal -> Either Errors.Error (Core.Type, t0)
typeOfLiteral cx tx typeArgs lit =
let t = Core.TypeLiteral (Reflect.literalType lit)
in (Eithers.bind (applyTypeArgumentsToType cx tx typeArgs t) (\applied -> Right (applied, cx)))
-- | Reconstruct the type of a map (Either/InferenceContext version)
typeOfMap :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> M.Map Core.Term Core.Term -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfMap cx tx typeArgs m =
let mapArityErr =
Left (Errors.ErrorChecking (Checking.CheckingErrorTypeArityMismatch (Checking.TypeArityMismatchError {
Checking.typeArityMismatchErrorType = (Core.TypeMap (Core.MapType {
Core.mapTypeKeys = Core.TypeUnit,
Core.mapTypeValues = Core.TypeUnit})),
Checking.typeArityMismatchErrorExpectedArity = 2,
Checking.typeArityMismatchErrorActualArity = (Lists.length typeArgs),
Checking.typeArityMismatchErrorTypeArguments = typeArgs})))
in (Logic.ifElse (Maps.null m) (Logic.ifElse (Equality.equal (Lists.length typeArgs) 2) (Optionals.cases (Lists.uncons typeArgs) mapArityErr (\uc0 ->
let ta0 = Pairs.first uc0
in (Optionals.cases (Lists.uncons (Pairs.second uc0)) mapArityErr (\uc1 ->
let ta1 = Pairs.first uc1
in (Right (
Core.TypeMap (Core.MapType {
Core.mapTypeKeys = ta0,
Core.mapTypeValues = ta1}),
cx)))))) mapArityErr) (
let pairs = Maps.toList m
keyFoldResult =
Lists.foldl (\acc -> \p -> Eithers.bind acc (\accR ->
let types = Pairs.first accR
cxA = Pairs.second accR
in (Eithers.bind (typeOf cxA tx [] (Pairs.first p)) (\tResult ->
let t = Pairs.first tResult
cxB = Pairs.second tResult
in (Right (Lists.concat2 types (Lists.pure t), cxB)))))) (Right ([], cx)) pairs
in (Eithers.bind keyFoldResult (\keyFoldR ->
let keyTypes = Pairs.first keyFoldR
cx2 = Pairs.second keyFoldR
in (Eithers.bind (checkSameType cx2 tx "map keys" keyTypes) (\kt ->
let valFoldResult =
Lists.foldl (\acc -> \p -> Eithers.bind acc (\accR ->
let types = Pairs.first accR
cxA = Pairs.second accR
in (Eithers.bind (typeOf cxA tx [] (Pairs.second p)) (\tResult ->
let t = Pairs.first tResult
cxB = Pairs.second tResult
in (Right (Lists.concat2 types (Lists.pure t), cxB)))))) (Right ([], cx2)) pairs
in (Eithers.bind valFoldResult (\valFoldR ->
let valTypes = Pairs.first valFoldR
cx3 = Pairs.second valFoldR
in (Eithers.bind (checkSameType cx3 tx "map values" valTypes) (\vt -> Eithers.bind (applyTypeArgumentsToType cx3 tx typeArgs (Core.TypeMap (Core.MapType {
Core.mapTypeKeys = kt,
Core.mapTypeValues = vt}))) (\applied -> Right (applied, cx3))))))))))))
-- | Reconstruct the type of an optional value (Either/InferenceContext version)
typeOfMaybe :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Maybe Core.Term -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfMaybe cx tx typeArgs mt =
let forNothing =
let n = Lists.length typeArgs
maybeArityErr =
Left (Errors.ErrorChecking (Checking.CheckingErrorTypeArityMismatch (Checking.TypeArityMismatchError {
Checking.typeArityMismatchErrorType = (Core.TypeOptional Core.TypeUnit),
Checking.typeArityMismatchErrorExpectedArity = 1,
Checking.typeArityMismatchErrorActualArity = n,
Checking.typeArityMismatchErrorTypeArguments = typeArgs})))
in (Logic.ifElse (Equality.equal n 1) (Optionals.cases (Lists.maybeHead typeArgs) maybeArityErr (\ta0 -> Right (Core.TypeOptional ta0, cx))) maybeArityErr)
forJust =
\term -> Eithers.bind (typeOf cx tx [] term) (\tResult ->
let termType = Pairs.first tResult
cx2 = Pairs.second tResult
t = Core.TypeOptional termType
in (Eithers.bind (applyTypeArgumentsToType cx2 tx typeArgs t) (\applied -> Right (applied, cx2))))
in (Optionals.cases mt forNothing forJust)
-- | Reconstruct the type of a pair (Either/InferenceContext version)
typeOfPair :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> (Core.Term, Core.Term) -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfPair cx tx typeArgs p =
let n = Lists.length typeArgs
in (Logic.ifElse (Equality.equal n 2) (
let pairFst = Pairs.first p
pairSnd = Pairs.second p
in (Eithers.bind (typeOf cx tx [] pairFst) (\result1 ->
let firstType = Pairs.first result1
cx2 = Pairs.second result1
in (Eithers.bind (typeOf cx2 tx [] pairSnd) (\result2 ->
let secondType = Pairs.first result2
cx3 = Pairs.second result2
in (Right (
Core.TypePair (Core.PairType {
Core.pairTypeFirst = firstType,
Core.pairTypeSecond = secondType}),
cx3))))))) (Left (Errors.ErrorChecking (Checking.CheckingErrorTypeArityMismatch (Checking.TypeArityMismatchError {
Checking.typeArityMismatchErrorType = (Core.TypePair (Core.PairType {
Core.pairTypeFirst = Core.TypeUnit,
Core.pairTypeSecond = Core.TypeUnit})),
Checking.typeArityMismatchErrorExpectedArity = 2,
Checking.typeArityMismatchErrorActualArity = n,
Checking.typeArityMismatchErrorTypeArguments = typeArgs})))))
-- | Reconstruct the type of a primitive function (Either/InferenceContext version)
typeOfPrimitive :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Name -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfPrimitive cx tx typeArgs name =
let rawTs =
Optionals.map (\_p -> Scoping.termSignatureToTypeScheme (Packaging.primitiveDefinitionSignature (Graph.primitiveDefinition _p))) (Maps.lookup name (Graph.graphPrimitives tx))
in (Optionals.cases rawTs (Left (Errors.ErrorUndefinedTermVariable (ErrorCore.UndefinedTermVariableError {
ErrorCore.undefinedTermVariableErrorLocation = (Paths.SubtermPath []),
ErrorCore.undefinedTermVariableErrorName = name}))) (\tsRaw ->
let instResult = Resolution.instantiateTypeScheme cx tsRaw
ts = Pairs.first instResult
cx2 = Pairs.second instResult
t = Scoping.typeSchemeToFType ts
in (Eithers.bind (applyTypeArgumentsToType cx2 tx typeArgs t) (\applied -> Right (applied, cx2)))))
-- | Reconstruct the type of a record projection (Either/InferenceContext version)
typeOfProjection :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Projection -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfProjection cx tx typeArgs p =
let tname = Core.projectionTypeName p
fname = Core.projectionFieldName p
in (Eithers.bind (Resolution.requireSchemaType cx (Graph.graphSchemaTypes tx) tname) (\schemaResult ->
let schemaType = Pairs.first schemaResult
cx2 = Pairs.second schemaResult
svars = Core.typeSchemeVariables schemaType
sbody = Core.typeSchemeBody schemaType
in (Eithers.bind (ExtractCore.recordType tname sbody) (\sfields -> Eithers.bind (Resolution.findFieldType cx2 fname sfields) (\ftyp ->
let subst = Typing.TypeSubst (Maps.fromList (Lists.zip svars typeArgs))
sftyp = Substitution.substInType subst ftyp
in (Right (
Core.TypeFunction (Core.FunctionType {
Core.functionTypeDomain = (Resolution.nominalApplication tname typeArgs),
Core.functionTypeCodomain = sftyp}),
cx2)))))))
-- | Reconstruct the type of a record (Either/InferenceContext version)
typeOfRecord :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Record -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfRecord cx tx typeArgs record =
let tname = Core.recordTypeName record
fields = Core.recordFields record
foldResult =
Lists.foldl (\acc -> \term -> Eithers.bind acc (\accR ->
let types = Pairs.first accR
cxA = Pairs.second accR
in (Eithers.bind (typeOf cxA tx [] term) (\tResult ->
let t = Pairs.first tResult
cxB = Pairs.second tResult
in (Right (Lists.concat2 types (Lists.pure t), cxB)))))) (Right ([], cx)) (Lists.map Core.fieldTerm fields)
in (Eithers.bind foldResult (\foldR ->
let cx2 = Pairs.second foldR
in (Right (Resolution.nominalApplication tname typeArgs, cx2))))
-- | Reconstruct the type of a set (Either/InferenceContext version)
typeOfSet :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> S.Set Core.Term -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfSet cx tx typeArgs els =
let setArityErr =
Left (Errors.ErrorChecking (Checking.CheckingErrorTypeArityMismatch (Checking.TypeArityMismatchError {
Checking.typeArityMismatchErrorType = (Core.TypeSet Core.TypeUnit),
Checking.typeArityMismatchErrorExpectedArity = 1,
Checking.typeArityMismatchErrorActualArity = (Lists.length typeArgs),
Checking.typeArityMismatchErrorTypeArguments = typeArgs})))
in (Logic.ifElse (Sets.null els) (Logic.ifElse (Equality.equal (Lists.length typeArgs) 1) (Optionals.cases (Lists.maybeHead typeArgs) setArityErr (\ta0 -> Right (Core.TypeSet ta0, cx))) setArityErr) (
let foldResult =
Lists.foldl (\acc -> \term -> Eithers.bind acc (\accR ->
let types = Pairs.first accR
cxA = Pairs.second accR
in (Eithers.bind (typeOf cxA tx [] term) (\tResult ->
let t = Pairs.first tResult
cxB = Pairs.second tResult
in (Right (Lists.concat2 types (Lists.pure t), cxB)))))) (Right ([], cx)) (Sets.toList els)
in (Eithers.bind foldResult (\foldR ->
let eltypes = Pairs.first foldR
cx2 = Pairs.second foldR
in (Eithers.bind (checkSameType cx2 tx "set elements" eltypes) (\unifiedType -> Right (Core.TypeSet unifiedType, cx2)))))))
-- | Check the type of a term
typeOfTerm :: Typing.InferenceContext -> Graph.Graph -> Core.Term -> Either Errors.Error Core.Type
typeOfTerm cx g term = Eithers.map Pairs.first (typeOf cx g [] term)
-- | Reconstruct the type of a type application term (Either/InferenceContext version)
typeOfTypeApplication :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.TypeApplicationTerm -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfTypeApplication cx tx typeArgs tyapp =
let body = Core.typeApplicationTermBody tyapp
t = Core.typeApplicationTermType tyapp
in (typeOf cx tx (Lists.cons t typeArgs) body)
-- | Reconstruct the type of a type lambda (type abstraction) term (Either/InferenceContext version)
typeOfTypeLambda :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.TypeLambda -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfTypeLambda cx tx typeArgs tl =
let v = Core.typeLambdaParameter tl
body = Core.typeLambdaBody tl
vars = Graph.graphTypeVariables tx
tx2 =
Graph.Graph {
Graph.graphBoundTerms = (Graph.graphBoundTerms tx),
Graph.graphBoundTypes = (Graph.graphBoundTypes tx),
Graph.graphClassConstraints = (Graph.graphClassConstraints tx),
Graph.graphLambdaVariables = (Graph.graphLambdaVariables tx),
Graph.graphMetadata = (Graph.graphMetadata tx),
Graph.graphPrimitives = (Graph.graphPrimitives tx),
Graph.graphSchemaTypes = (Graph.graphSchemaTypes tx),
Graph.graphTypeVariables = (Sets.insert v vars)}
in (Eithers.bind (typeOf cx tx2 [] body) (\result1 ->
let t1 = Pairs.first result1
cx2 = Pairs.second result1
in (Eithers.bind (applyTypeArgumentsToType cx2 tx typeArgs (Core.TypeForall (Core.ForallType {
Core.forallTypeParameter = v,
Core.forallTypeBody = t1}))) (\applied -> Right (applied, cx2)))))
-- | Reconstruct the type of the unit term (Either/InferenceContext version)
typeOfUnit :: t0 -> Graph.Graph -> [Core.Type] -> Either Errors.Error (Core.Type, t0)
typeOfUnit cx tx typeArgs =
Eithers.bind (applyTypeArgumentsToType cx tx typeArgs Core.TypeUnit) (\applied -> Right (applied, cx))
-- | Reconstruct the type of an unwrap operation (Either/InferenceContext version)
typeOfUnwrap :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Name -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfUnwrap cx tx typeArgs tname =
Eithers.bind (Resolution.requireSchemaType cx (Graph.graphSchemaTypes tx) tname) (\schemaResult ->
let schemaType = Pairs.first schemaResult
cx2 = Pairs.second schemaResult
svars = Core.typeSchemeVariables schemaType
sbody = Core.typeSchemeBody schemaType
in (Eithers.bind (ExtractCore.wrappedType tname sbody) (\wrapped ->
let subst = Typing.TypeSubst (Maps.fromList (Lists.zip svars typeArgs))
swrapped = Substitution.substInType subst wrapped
in (Right (
Core.TypeFunction (Core.FunctionType {
Core.functionTypeDomain = (Resolution.nominalApplication tname typeArgs),
Core.functionTypeCodomain = swrapped}),
cx2)))))
-- | Reconstruct the type of a variable (Either/InferenceContext version)
typeOfVariable :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.Name -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfVariable cx tx typeArgs name =
let rawTypeScheme = Maps.lookup name (Graph.graphBoundTypes tx)
forScheme =
\ts ->
let tResult =
Logic.ifElse (Lists.null typeArgs) (Resolution.instantiateType cx (Scoping.typeSchemeToFType ts)) (Scoping.typeSchemeToFType ts, cx)
t = Pairs.first tResult
cx2 = Pairs.second tResult
in (Eithers.bind (applyTypeArgumentsToType cx2 tx typeArgs t) (\applied -> Right (applied, cx2)))
in (Optionals.cases rawTypeScheme (Optionals.cases (Optionals.map (\_p -> Scoping.termSignatureToTypeScheme (Packaging.primitiveDefinitionSignature (Graph.primitiveDefinition _p))) (Maps.lookup name (Graph.graphPrimitives tx))) (Left (Errors.ErrorUntypedTermVariable (ErrorCore.UntypedTermVariableError {
ErrorCore.untypedTermVariableErrorLocation = (Paths.SubtermPath []),
ErrorCore.untypedTermVariableErrorName = name}))) forScheme) forScheme)
-- | Reconstruct the type of a wrapped term (Either/InferenceContext version)
typeOfWrappedTerm :: Typing.InferenceContext -> Graph.Graph -> [Core.Type] -> Core.WrappedTerm -> Either Errors.Error (Core.Type, Typing.InferenceContext)
typeOfWrappedTerm cx tx typeArgs wt =
let tname = Core.wrappedTermTypeName wt
body = Core.wrappedTermBody wt
in (Eithers.bind (typeOf cx tx [] body) (\result ->
let cx2 = Pairs.second result
in (Right (Resolution.nominalApplication tname typeArgs, cx2))))
-- | Check whether a list of types are effectively equal, disregarding type aliases and free type variable naming. Also treats free type variables (not in schema) as wildcards, since inference has already verified consistency.
typesAllEffectivelyEqual :: Graph.Graph -> [Core.Type] -> Bool
typesAllEffectivelyEqual tx tlist =
let types = Graph.graphSchemaTypes tx
containsFreeVar =
\t ->
let allVars = Variables.freeVariablesInTypeSimple t
schemaNames = Sets.fromList (Maps.keys types)
in (Logic.not (Sets.null (Sets.difference allVars schemaNames)))
anyContainsFreeVar = Lists.foldl (\acc -> \t -> Logic.or acc (containsFreeVar t)) False tlist
in (Logic.ifElse anyContainsFreeVar True (Logic.ifElse (allEqual (Lists.map (\t -> normalizeTypeFreeVars t) tlist)) True (allEqual (Lists.map (\t -> normalizeTypeFreeVars (Strip.deannotateTypeRecursive (Dependencies.replaceTypedefs types t))) tlist))))
-- | Check whether two types are effectively equal, disregarding type aliases, forall quantifiers, and treating in-scope type variables as wildcards
typesEffectivelyEqual :: Graph.Graph -> Core.Type -> Core.Type -> Bool
typesEffectivelyEqual tx t1 t2 =
Logic.or (containsInScopeTypeVars tx t1) (Logic.or (containsInScopeTypeVars tx t2) (typesAllEffectivelyEqual tx [
Resolution.fullyStripAndNormalizeType t1,
(Resolution.fullyStripAndNormalizeType t2)]))