packages feed

hydra-kernel-0.17.0: src/main/haskell/Hydra/Validate/Core.hs

-- Note: this is an automatically generated file. Do not edit.

-- | Validation functions for core terms and types

module Hydra.Validate.Core where

import qualified Hydra.Annotations as Annotations
import qualified Hydra.Ast as Ast
import qualified Hydra.Coders as Coders
import qualified Hydra.Core as Core
import qualified Hydra.Docs as Docs
import qualified Hydra.Error.Checking as Checking
import qualified Hydra.Error.Core as ErrorCore
import qualified Hydra.Error.File as ErrorFile
import qualified Hydra.Error.Packaging as ErrorPackaging
import qualified Hydra.Error.System as ErrorSystem
import qualified Hydra.Errors as Errors
import qualified Hydra.File as File
import qualified Hydra.Graph as Graph
import qualified Hydra.Json.Model as Model
import qualified Hydra.Overlay.Haskell.Lib.Equality as Equality
import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
import qualified Hydra.Overlay.Haskell.Lib.Logic as Logic
import qualified Hydra.Overlay.Haskell.Lib.Maps as Maps
import qualified Hydra.Overlay.Haskell.Lib.Optionals as Optionals
import qualified Hydra.Overlay.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Overlay.Haskell.Lib.Sets as Sets
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.Rewriting as Rewriting
import qualified Hydra.System as System
import qualified Hydra.Tabular as Tabular
import qualified Hydra.Testing as Testing
import qualified Hydra.Time as Time
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.Set as S

-- | Append a rule-tagged InvalidTermError finding to a ValidationResult, classifying as error or warning per the profile and respecting maxErrors/maxWarnings bounds.
appendFinding :: Validation.ValidationProfile -> Validation.ValidationResult t0 -> Maybe (Core.Name, t0) -> Validation.ValidationResult t0
appendFinding p acc finding =
    Optionals.cases finding acc (\rp ->
      let ruleName = Pairs.first rp
          payload = Pairs.second rp
          errs = Validation.validationResultErrors acc
          wrns = Validation.validationResultWarnings acc
      in (Logic.ifElse (Sets.member ruleName (Validation.validationProfileErrorRules p)) (Logic.ifElse (Equality.lt (Lists.length errs) (Validation.validationProfileMaxErrors p)) (Validation.ValidationResult {
        Validation.validationResultErrors = (Lists.concat2 errs (Lists.singleton payload)),
        Validation.validationResultWarnings = wrns}) acc) (Logic.ifElse (Sets.member ruleName (Validation.validationProfileWarningRules p)) (Logic.ifElse (Equality.lt (Lists.length wrns) (Validation.validationProfileMaxWarnings p)) (Validation.ValidationResult {
        Validation.validationResultErrors = errs,
        Validation.validationResultWarnings = (Lists.concat2 wrns (Lists.singleton payload))}) acc) acc)))

-- | Append a rule-tagged InvalidTypeError finding to a ValidationResult, classifying as error or warning per the profile and respecting maxErrors/maxWarnings bounds.
appendFindingType :: Validation.ValidationProfile -> Validation.ValidationResult t0 -> Maybe (Core.Name, t0) -> Validation.ValidationResult t0
appendFindingType p acc finding =
    Optionals.cases finding acc (\rp ->
      let ruleName = Pairs.first rp
          payload = Pairs.second rp
          errs = Validation.validationResultErrors acc
          wrns = Validation.validationResultWarnings acc
      in (Logic.ifElse (Sets.member ruleName (Validation.validationProfileErrorRules p)) (Logic.ifElse (Equality.lt (Lists.length errs) (Validation.validationProfileMaxErrors p)) (Validation.ValidationResult {
        Validation.validationResultErrors = (Lists.concat2 errs (Lists.singleton payload)),
        Validation.validationResultWarnings = wrns}) acc) (Logic.ifElse (Sets.member ruleName (Validation.validationProfileWarningRules p)) (Logic.ifElse (Equality.lt (Lists.length wrns) (Validation.validationProfileMaxWarnings p)) (Validation.ValidationResult {
        Validation.validationResultErrors = errs,
        Validation.validationResultWarnings = (Lists.concat2 wrns (Lists.singleton payload))}) acc) acc)))

-- | Check for duplicate binding names in a list of bindings
checkDuplicateBindings :: Paths.SubtermPath -> [Core.Binding] -> Maybe ErrorCore.InvalidTermError
checkDuplicateBindings path bindings =

      let names = Lists.map Core.bindingName bindings
          dup = findDuplicate names
      in (Optionals.map (\name -> ErrorCore.InvalidTermErrorDuplicateBinding (ErrorCore.DuplicateBindingError {
        ErrorCore.duplicateBindingErrorLocation = path,
        ErrorCore.duplicateBindingErrorName = name})) dup)

-- | Check for duplicate field names in a list of field types
checkDuplicateFieldTypes :: [Core.FieldType] -> (Core.Name -> Maybe t0) -> Maybe t0
checkDuplicateFieldTypes fields mkError =

      let names = Lists.map Core.fieldTypeName fields
          dup = findDuplicateFieldType names
      in (Optionals.cases dup Nothing (\name -> mkError name))

-- | Check for duplicate field names in a list of fields
checkDuplicateFields :: Paths.SubtermPath -> [Core.Name] -> Maybe ErrorCore.InvalidTermError
checkDuplicateFields path names =

      let dup = findDuplicate names
      in (Optionals.map (\name -> ErrorCore.InvalidTermErrorDuplicateField (ErrorCore.DuplicateFieldError {
        ErrorCore.duplicateFieldErrorLocation = path,
        ErrorCore.duplicateFieldErrorName = name})) dup)

-- | Check that a literal value's type matches an expected literal type
checkLiteral :: Core.LiteralType -> Core.Literal -> Maybe ErrorCore.InvalidLiteralError
checkLiteral expected value =

      let actual = Reflect.literalType value
      in (Logic.ifElse (Equality.equal expected actual) Nothing (Just (ErrorCore.InvalidLiteralErrorTypeMismatch (ErrorCore.LiteralTypeMismatchError {
        ErrorCore.literalTypeMismatchErrorExpectedType = expected,
        ErrorCore.literalTypeMismatchErrorActualType = actual}))))

-- | Check if any name in a list shadows a variable already in scope
checkShadowing :: Paths.SubtermPath -> Graph.Graph -> [Core.Name] -> Maybe ErrorCore.InvalidTermError
checkShadowing path cx names =

      let result =
              Lists.foldl (\acc -> \name -> Optionals.cases acc (Logic.ifElse (Logic.or (Optionals.isGiven (Maps.lookup name (Graph.graphBoundTerms cx))) (Sets.member name (Graph.graphLambdaVariables cx))) (Just (ErrorCore.InvalidTermErrorTermVariableShadowing (ErrorCore.TermVariableShadowingError {
                ErrorCore.termVariableShadowingErrorLocation = path,
                ErrorCore.termVariableShadowingErrorName = name}))) Nothing) (\_ -> acc)) Nothing names
      in result

-- | Check a single term node for validation errors. Rules disabled by the profile are not evaluated.
checkTerm :: Validation.ValidationProfile -> Bool -> Paths.SubtermPath -> Graph.Graph -> Core.Term -> Maybe (Core.Name, ErrorCore.InvalidTermError)
checkTerm p typed path cx term =
    case term of
      Core.TermAnnotated v0 ->
        let body = Core.annotatedTermBody v0
            annMap = Annotations.getAnnotationMap (Core.annotatedTermAnnotation v0)
        in (firstFinding [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyTermAnnotation")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyTermAnnotation", f)) (Logic.ifElse (Maps.null annMap) (Just (ErrorCore.InvalidTermErrorEmptyTermAnnotation (ErrorCore.EmptyTermAnnotationError {
            ErrorCore.emptyTermAnnotationErrorLocation = path}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.nestedTermAnnotation")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.nestedTermAnnotation", f)) (case body of
            Core.TermAnnotated _ -> Just (ErrorCore.InvalidTermErrorNestedTermAnnotation (ErrorCore.NestedTermAnnotationError {
              ErrorCore.nestedTermAnnotationErrorLocation = path}))
            _ -> Nothing)) Nothing)])
      Core.TermApplication v0 ->
        let fun = Core.applicationFunction v0
            arg = Core.applicationArgument v0
        in (firstFinding [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.constantCondition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.constantCondition", f)) (case fun of
            Core.TermVariable v1 -> Logic.ifElse (Equality.equal (Core.unName v1) "hydra.lib.logic.ifElse") (case arg of
              Core.TermLiteral v2 -> case v2 of
                Core.LiteralBoolean v3 -> Just (ErrorCore.InvalidTermErrorConstantCondition (ErrorCore.ConstantConditionError {
                  ErrorCore.constantConditionErrorLocation = path,
                  ErrorCore.constantConditionErrorValue = v3}))
                _ -> Nothing
              _ -> Nothing) Nothing
            _ -> Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.selfApplication")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.selfApplication", f)) (case fun of
            Core.TermVariable v1 -> case arg of
              Core.TermVariable v2 -> Logic.ifElse (Equality.equal v1 v2) (Just (ErrorCore.InvalidTermErrorSelfApplication (ErrorCore.SelfApplicationError {
                ErrorCore.selfApplicationErrorLocation = path,
                ErrorCore.selfApplicationErrorName = v1}))) Nothing
              _ -> Nothing
            _ -> Nothing)) Nothing),
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.unnecessaryIdentityApplication")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.unnecessaryIdentityApplication", f)) (case fun of
            Core.TermLambda v1 ->
              let param = Core.lambdaParameter v1
                  body = Core.lambdaBody v1
              in case body of
                Core.TermVariable v2 -> Logic.ifElse (Equality.equal param v2) (Just (ErrorCore.InvalidTermErrorUnnecessaryIdentityApplication (ErrorCore.UnnecessaryIdentityApplicationError {
                  ErrorCore.unnecessaryIdentityApplicationErrorLocation = path}))) Nothing
                _ -> Nothing
            _ -> Nothing)) Nothing),
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.redundantWrapUnwrap")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.redundantWrapUnwrap", f)) (case fun of
            Core.TermUnwrap v1 -> case arg of
              Core.TermWrap v2 ->
                let wrapName = Core.wrappedTermTypeName v2
                in (Logic.ifElse (Equality.equal v1 wrapName) (Just (ErrorCore.InvalidTermErrorRedundantWrapUnwrap (ErrorCore.RedundantWrapUnwrapError {
                  ErrorCore.redundantWrapUnwrapErrorLocation = path,
                  ErrorCore.redundantWrapUnwrapErrorTypeName = v1}))) Nothing)
              _ -> Nothing
            _ -> Nothing)) Nothing)])
      Core.TermRecord v0 ->
        let tname = Core.recordTypeName v0
            flds = Core.recordFields v0
        in (firstFinding [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm", f)) (Logic.ifElse (Equality.equal (Core.unName tname) "") (Just (ErrorCore.InvalidTermErrorEmptyTypeNameInTerm (ErrorCore.EmptyTypeNameInTermError {
            ErrorCore.emptyTypeNameInTermErrorLocation = path}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.duplicateField")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.duplicateField", f)) (checkDuplicateFields path (Lists.map Core.fieldName flds))) Nothing)])
      Core.TermLet v0 ->
        let bindings = Core.letBindings v0
            names = Lists.map Core.bindingName bindings
        in (firstFinding [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyLetBindings")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyLetBindings", f)) (Logic.ifElse (Lists.null bindings) (Just (ErrorCore.InvalidTermErrorEmptyLetBindings (ErrorCore.EmptyLetBindingsError {
            ErrorCore.emptyLetBindingsErrorLocation = path}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.duplicateBinding")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.duplicateBinding", f)) (checkDuplicateBindings path bindings)) Nothing),
          Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.invalidLetBindingName")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.invalidLetBindingName", f)) (firstError (Lists.map (\bname -> Logic.ifElse (isValidName bname) Nothing (Just (ErrorCore.InvalidTermErrorInvalidLetBindingName (ErrorCore.InvalidLetBindingNameError {
            ErrorCore.invalidLetBindingNameErrorLocation = path,
            ErrorCore.invalidLetBindingNameErrorName = bname})))) names))) Nothing),
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInBindingType")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInBindingType", f)) (Logic.ifElse typed (firstError (Lists.map (\b -> Optionals.cases (Core.bindingTypeScheme b) Nothing (\ts -> checkUndefinedTypeVariablesInTypeScheme path cx ts (\uvName -> Just (ErrorCore.InvalidTermErrorUndefinedTypeVariableInBindingType (ErrorCore.UndefinedTypeVariableInBindingTypeError {
            ErrorCore.undefinedTypeVariableInBindingTypeErrorLocation = path,
            ErrorCore.undefinedTypeVariableInBindingTypeErrorName = uvName}))))) bindings)) Nothing)) Nothing)])
      Core.TermInject v0 ->
        let tname = Core.injectionTypeName v0
        in (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm", f)) (Logic.ifElse (Equality.equal (Core.unName tname) "") (Just (ErrorCore.InvalidTermErrorEmptyTypeNameInTerm (ErrorCore.EmptyTypeNameInTermError {
          ErrorCore.emptyTypeNameInTermErrorLocation = path}))) Nothing)) Nothing)
      Core.TermLambda v0 ->
        let paramName = Core.lambdaParameter v0
        in (firstFinding [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.termVariableShadowing")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.termVariableShadowing", f)) (Logic.ifElse (Optionals.isGiven (Maps.lookup paramName (Graph.graphBoundTerms cx))) (Just (ErrorCore.InvalidTermErrorTermVariableShadowing (ErrorCore.TermVariableShadowingError {
            ErrorCore.termVariableShadowingErrorLocation = path,
            ErrorCore.termVariableShadowingErrorName = paramName}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.invalidLambdaParameterName")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.invalidLambdaParameterName", f)) (Logic.ifElse (isValidName paramName) Nothing (Just (ErrorCore.InvalidTermErrorInvalidLambdaParameterName (ErrorCore.InvalidLambdaParameterNameError {
            ErrorCore.invalidLambdaParameterNameErrorLocation = path,
            ErrorCore.invalidLambdaParameterNameErrorName = paramName}))))) Nothing),
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInLambdaDomain")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInLambdaDomain", f)) (Logic.ifElse typed (Optionals.cases (Core.lambdaDomain v0) Nothing (\dom -> checkUndefinedTypeVariablesInType path cx dom (\uvName -> Just (ErrorCore.InvalidTermErrorUndefinedTypeVariableInLambdaDomain (ErrorCore.UndefinedTypeVariableInLambdaDomainError {
            ErrorCore.undefinedTypeVariableInLambdaDomainErrorLocation = path,
            ErrorCore.undefinedTypeVariableInLambdaDomainErrorName = uvName}))))) Nothing)) Nothing)])
      Core.TermProject v0 ->
        let tname = Core.projectionTypeName v0
        in (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm", f)) (Logic.ifElse (Equality.equal (Core.unName tname) "") (Just (ErrorCore.InvalidTermErrorEmptyTypeNameInTerm (ErrorCore.EmptyTypeNameInTermError {
          ErrorCore.emptyTypeNameInTermErrorLocation = path}))) Nothing)) Nothing)
      Core.TermCases v0 ->
        let tname = Core.caseStatementTypeName v0
            csDefault = Core.caseStatementDefault v0
            csCases = Core.caseStatementCases v0
        in (firstFinding [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm", f)) (Logic.ifElse (Equality.equal (Core.unName tname) "") (Just (ErrorCore.InvalidTermErrorEmptyTypeNameInTerm (ErrorCore.EmptyTypeNameInTermError {
            ErrorCore.emptyTypeNameInTermErrorLocation = path}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyCaseStatement")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyCaseStatement", f)) (Logic.ifElse (Logic.and (Lists.null csCases) (Optionals.isNone csDefault)) (Just (ErrorCore.InvalidTermErrorEmptyCaseStatement (ErrorCore.EmptyCaseStatementError {
            ErrorCore.emptyCaseStatementErrorLocation = path,
            ErrorCore.emptyCaseStatementErrorTypeName = tname}))) Nothing)) Nothing),
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.duplicateField")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.duplicateField", f)) (checkDuplicateFields path (Lists.map Core.caseAlternativeName csCases))) Nothing)])
      Core.TermTypeApplication v0 -> Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInTypeApplication")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInTypeApplication", f)) (Logic.ifElse typed (checkUndefinedTypeVariablesInType path cx (Core.typeApplicationTermType v0) (\uvName -> Just (ErrorCore.InvalidTermErrorUndefinedTypeVariableInTypeApplication (ErrorCore.UndefinedTypeVariableInTypeApplicationError {
        ErrorCore.undefinedTypeVariableInTypeApplicationErrorLocation = path,
        ErrorCore.undefinedTypeVariableInTypeApplicationErrorName = uvName})))) Nothing)) Nothing
      Core.TermTypeLambda v0 ->
        let tvName = Core.typeLambdaParameter v0
        in (firstFinding [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.typeVariableShadowingInTypeLambda")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.typeVariableShadowingInTypeLambda", f)) (Logic.ifElse (Sets.member tvName (Sets.delete tvName (Graph.graphTypeVariables cx))) (Just (ErrorCore.InvalidTermErrorTypeVariableShadowingInTypeLambda (ErrorCore.TypeVariableShadowingInTypeLambdaError {
            ErrorCore.typeVariableShadowingInTypeLambdaErrorLocation = path,
            ErrorCore.typeVariableShadowingInTypeLambdaErrorName = tvName}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.invalidTypeLambdaParameterName")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.invalidTypeLambdaParameterName", f)) (Logic.ifElse (isValidName tvName) Nothing (Just (ErrorCore.InvalidTermErrorInvalidTypeLambdaParameterName (ErrorCore.InvalidTypeLambdaParameterNameError {
            ErrorCore.invalidTypeLambdaParameterNameErrorLocation = path,
            ErrorCore.invalidTypeLambdaParameterNameErrorName = tvName}))))) Nothing)])
      Core.TermVariable v0 -> Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.undefinedTermVariable")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.undefinedTermVariable", f)) (Logic.ifElse (Logic.or (Optionals.isGiven (Maps.lookup v0 (Graph.graphBoundTerms cx))) (Logic.or (Sets.member v0 (Graph.graphLambdaVariables cx)) (Optionals.isGiven (Maps.lookup v0 (Graph.graphPrimitives cx))))) Nothing (Just (ErrorCore.InvalidTermErrorUndefinedTermVariable (ErrorCore.UndefinedTermVariableError {
        ErrorCore.undefinedTermVariableErrorLocation = path,
        ErrorCore.undefinedTermVariableErrorName = v0}))))) Nothing
      Core.TermWrap v0 ->
        let tname = Core.wrappedTermTypeName v0
        in (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm", f)) (Logic.ifElse (Equality.equal (Core.unName tname) "") (Just (ErrorCore.InvalidTermErrorEmptyTypeNameInTerm (ErrorCore.EmptyTypeNameInTermError {
          ErrorCore.emptyTypeNameInTermErrorLocation = path}))) Nothing)) Nothing)
      _ -> Nothing

-- | Check a type for type variables not bound in the current scope
checkUndefinedTypeVariablesInType :: t0 -> Graph.Graph -> Core.Type -> (Core.Name -> Maybe t1) -> Maybe t1
checkUndefinedTypeVariablesInType path cx typ mkError =

      let freeVars = Variables.freeVariablesInType typ
          undefined = Sets.difference freeVars (Graph.graphTypeVariables cx)
      in (Optionals.cases (Lists.maybeHead (Sets.toList undefined)) Nothing (\firstUndefined -> mkError firstUndefined))

-- | Check a type scheme for type variables not bound by the scheme or the current scope
checkUndefinedTypeVariablesInTypeScheme :: t0 -> Graph.Graph -> Core.TypeScheme -> (Core.Name -> Maybe t1) -> Maybe t1
checkUndefinedTypeVariablesInTypeScheme path cx ts mkError =

      let freeVars = Variables.freeVariablesInTypeScheme ts
          undefined = Sets.difference freeVars (Graph.graphTypeVariables cx)
      in (Optionals.cases (Lists.maybeHead (Sets.toList undefined)) Nothing (\firstUndefined -> mkError firstUndefined))

-- | Return an error if the given type is TypeVoid
checkVoid :: Core.Type -> Maybe ErrorCore.InvalidTypeError
checkVoid typ =
    case typ of
      Core.TypeVoid -> Just (ErrorCore.InvalidTypeErrorVoidInNonBottomPosition (ErrorCore.VoidInNonBottomPositionError {
        ErrorCore.voidInNonBottomPositionErrorLocation = (Paths.SubtermPath [])}))
      _ -> Nothing

-- | True iff the given rule name appears in the profile's errorRules or warningRules.
enabled :: Validation.ValidationProfile -> Core.Name -> Bool
enabled p ruleName =
    Logic.or (Sets.member ruleName (Validation.validationProfileErrorRules p)) (Sets.member ruleName (Validation.validationProfileWarningRules p))

-- | Find the first duplicate name in a list
findDuplicate :: Ord t0 => ([t0] -> Maybe t0)
findDuplicate names =

      let result =
              Lists.foldl (\acc -> \name ->
                let seen = Pairs.first acc
                    dup = Pairs.second acc
                in (Optionals.cases dup (Logic.ifElse (Sets.member name seen) (seen, (Just name)) (Sets.insert name seen, Nothing)) (\_ -> acc))) (Sets.empty, Nothing) names
      in (Pairs.second result)

-- | Find the first duplicate name in a list (for field type validation)
findDuplicateFieldType :: Ord t0 => ([t0] -> Maybe t0)
findDuplicateFieldType names =

      let result =
              Lists.foldl (\acc -> \name ->
                let seen = Pairs.first acc
                    dup = Pairs.second acc
                in (Optionals.cases dup (Logic.ifElse (Sets.member name seen) (seen, (Just name)) (Sets.insert name seen, Nothing)) (\_ -> acc))) (Sets.empty, Nothing) names
      in (Pairs.second result)

-- | Return the first error from a list of optional errors, or nothing if all are valid
firstError :: [Maybe t0] -> Maybe t0
firstError checks = Lists.foldl (\acc -> \check -> Optionals.cases acc check (\_ -> acc)) Nothing checks

-- | Return the first rule-tagged finding from a list, or nothing if all are valid
firstFinding :: [Maybe t0] -> Maybe t0
firstFinding checks = Lists.foldl (\acc -> \check -> Optionals.cases acc check (\_ -> acc)) Nothing checks

-- | Return the first rule-tagged type finding from a list, or nothing if all are valid
firstFindingType :: [Maybe t0] -> Maybe t0
firstFindingType checks = Lists.foldl (\acc -> \check -> Optionals.cases acc check (\_ -> acc)) Nothing checks

-- | Return the first type error from a list of optional errors, or nothing if all are valid
firstTypeError :: [Maybe t0] -> Maybe t0
firstTypeError checks = Lists.foldl (\acc -> \check -> Optionals.cases acc check (\_ -> acc)) Nothing checks

-- | Check whether a name is valid at an introduction site. Currently rejects empty strings.
isValidName :: Core.Name -> Bool
isValidName name = Logic.not (Equality.equal (Core.unName name) "")

-- | The default validation profile for term and type validation, with every check classified as an error except InvalidTypeError.singleVariantUnion (warning); maxErrors=1, maxWarnings=20.
kernelDefaultCoreProfile :: Validation.ValidationProfile
kernelDefaultCoreProfile =
    Validation.ValidationProfile {
      Validation.validationProfileErrorRules = (Sets.fromList [
        Core.Name "hydra.error.core.InvalidTermError.constantCondition",
        (Core.Name "hydra.error.core.InvalidTermError.duplicateBinding"),
        (Core.Name "hydra.error.core.InvalidTermError.duplicateField"),
        (Core.Name "hydra.error.core.InvalidTermError.emptyCaseStatement"),
        (Core.Name "hydra.error.core.InvalidTermError.emptyLetBindings"),
        (Core.Name "hydra.error.core.InvalidTermError.emptyTermAnnotation"),
        (Core.Name "hydra.error.core.InvalidTermError.emptyTypeNameInTerm"),
        (Core.Name "hydra.error.core.InvalidTermError.invalidLambdaParameterName"),
        (Core.Name "hydra.error.core.InvalidTermError.invalidLetBindingName"),
        (Core.Name "hydra.error.core.InvalidTermError.invalidTypeLambdaParameterName"),
        (Core.Name "hydra.error.core.InvalidTermError.nestedTermAnnotation"),
        (Core.Name "hydra.error.core.InvalidTermError.redundantWrapUnwrap"),
        (Core.Name "hydra.error.core.InvalidTermError.selfApplication"),
        (Core.Name "hydra.error.core.InvalidTermError.termVariableShadowing"),
        (Core.Name "hydra.error.core.InvalidTermError.typeVariableShadowingInTypeLambda"),
        (Core.Name "hydra.error.core.InvalidTermError.undefinedTermVariable"),
        (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInBindingType"),
        (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInLambdaDomain"),
        (Core.Name "hydra.error.core.InvalidTermError.undefinedTypeVariableInTypeApplication"),
        (Core.Name "hydra.error.core.InvalidTermError.unknownPrimitiveName"),
        (Core.Name "hydra.error.core.InvalidTermError.unnecessaryIdentityApplication"),
        (Core.Name "hydra.error.core.InvalidTermError.untypedTermVariable"),
        (Core.Name "hydra.error.core.InvalidTypeError.duplicateRecordTypeFieldNames"),
        (Core.Name "hydra.error.core.InvalidTypeError.duplicateUnionTypeFieldNames"),
        (Core.Name "hydra.error.core.InvalidTypeError.emptyRecordType"),
        (Core.Name "hydra.error.core.InvalidTypeError.emptyTypeAnnotation"),
        (Core.Name "hydra.error.core.InvalidTypeError.emptyUnionType"),
        (Core.Name "hydra.error.core.InvalidTypeError.invalidForallParameterName"),
        (Core.Name "hydra.error.core.InvalidTypeError.invalidTypeSchemeVariableName"),
        (Core.Name "hydra.error.core.InvalidTypeError.nestedTypeAnnotation"),
        (Core.Name "hydra.error.core.InvalidTypeError.nonComparableMapKeyType"),
        (Core.Name "hydra.error.core.InvalidTypeError.nonComparableSetElementType"),
        (Core.Name "hydra.error.core.InvalidTypeError.typeVariableShadowingInForall"),
        (Core.Name "hydra.error.core.InvalidTypeError.undefinedTypeVariable"),
        (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")]),
      Validation.validationProfileWarningRules = (Sets.fromList [
        Core.Name "hydra.error.core.InvalidTypeError.singleVariantUnion"]),
      Validation.validationProfileMaxErrors = 1,
      Validation.validationProfileMaxWarnings = 20}

-- | Validate a term against the given ValidationProfile, returning a ValidationResult. Errors hard-stop traversal once maxErrors is reached; warnings are bounded by maxWarnings without causing termination.
term :: Validation.ValidationProfile -> Bool -> Graph.Graph -> Core.Term -> Validation.ValidationResult ErrorCore.InvalidTermError
term p typed g t =
    Rewriting.foldTermWithGraphAndPath (\recurse -> \path -> \cx -> \acc -> \trm -> Logic.ifElse (Equality.gte (Lists.length (Validation.validationResultErrors acc)) (Validation.validationProfileMaxErrors p)) acc (
      let acc1 = appendFinding p acc (checkTerm p typed (Paths.SubtermPath path) cx trm)
      in (Logic.ifElse (Equality.gte (Lists.length (Validation.validationResultErrors acc1)) (Validation.validationProfileMaxErrors p)) acc1 (recurse acc1 trm)))) g (Validation.ValidationResult {
      Validation.validationResultErrors = [],
      Validation.validationResultWarnings = []}) t

-- | Validate a type against the given ValidationProfile, threading a ValidationResult accumulator through subtypes. Errors hard-stop traversal once maxErrors is reached.
type_ :: Validation.ValidationProfile -> Validation.ValidationResult ErrorCore.InvalidTypeError -> S.Set Core.Name -> Core.Type -> Validation.ValidationResult ErrorCore.InvalidTypeError
type_ p acc boundVars typ =
    Logic.ifElse (Equality.gte (Lists.length (Validation.validationResultErrors acc)) (Validation.validationProfileMaxErrors p)) acc (
      let acc1 = appendFindingType p acc (validateTypeNode p boundVars typ)
      in (Logic.ifElse (Equality.gte (Lists.length (Validation.validationResultErrors acc1)) (Validation.validationProfileMaxErrors p)) acc1 (case typ of
        Core.TypeForall v0 ->
          let newBound = Sets.insert (Core.forallTypeParameter v0) boundVars
          in (type_ p acc1 newBound (Core.forallTypeBody v0))
        Core.TypeAnnotated v0 -> type_ p acc1 boundVars (Core.annotatedTypeBody v0)
        Core.TypeApplication v0 ->
          let acc2 = type_ p acc1 boundVars (Core.applicationTypeFunction v0)
          in (type_ p acc2 boundVars (Core.applicationTypeArgument v0))
        Core.TypeEffect v0 -> type_ p acc1 boundVars v0
        Core.TypeEither v0 ->
          let acc2 = type_ p acc1 boundVars (Core.eitherTypeLeft v0)
          in (type_ p acc2 boundVars (Core.eitherTypeRight v0))
        Core.TypeFunction v0 ->
          let acc2 = type_ p acc1 boundVars (Core.functionTypeDomain v0)
          in (type_ p acc2 boundVars (Core.functionTypeCodomain v0))
        Core.TypeList v0 -> type_ p acc1 boundVars v0
        Core.TypeMap v0 ->
          let acc2 = type_ p acc1 boundVars (Core.mapTypeKeys v0)
          in (type_ p acc2 boundVars (Core.mapTypeValues v0))
        Core.TypeOptional v0 -> type_ p acc1 boundVars v0
        Core.TypePair v0 ->
          let acc2 = type_ p acc1 boundVars (Core.pairTypeFirst v0)
          in (type_ p acc2 boundVars (Core.pairTypeSecond v0))
        Core.TypeRecord v0 -> Lists.foldl (\a -> \f -> type_ p a boundVars (Core.fieldTypeType f)) acc1 v0
        Core.TypeSet v0 -> type_ p acc1 boundVars v0
        Core.TypeUnion v0 -> Lists.foldl (\a -> \f -> type_ p a boundVars (Core.fieldTypeType f)) acc1 v0
        Core.TypeWrap v0 -> type_ p acc1 boundVars v0
        _ -> acc1)))

-- | Check a single type node for validation errors. Rules disabled by the profile are not evaluated.
validateTypeNode :: Validation.ValidationProfile -> S.Set Core.Name -> Core.Type -> Maybe (Core.Name, ErrorCore.InvalidTypeError)
validateTypeNode p boundVars typ =
    case typ of
      Core.TypeAnnotated v0 ->
        let body = Core.annotatedTypeBody v0
            annMap = Annotations.getAnnotationMap (Core.annotatedTypeAnnotation v0)
        in (firstFindingType [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.emptyTypeAnnotation")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.emptyTypeAnnotation", f)) (Logic.ifElse (Maps.null annMap) (Just (ErrorCore.InvalidTypeErrorEmptyTypeAnnotation (ErrorCore.EmptyTypeAnnotationError {
            ErrorCore.emptyTypeAnnotationErrorLocation = (Paths.SubtermPath [])}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.nestedTypeAnnotation")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.nestedTypeAnnotation", f)) (case body of
            Core.TypeAnnotated _ -> Just (ErrorCore.InvalidTypeErrorNestedTypeAnnotation (ErrorCore.NestedTypeAnnotationError {
              ErrorCore.nestedTypeAnnotationErrorLocation = (Paths.SubtermPath [])}))
            _ -> Nothing)) Nothing)])
      Core.TypeEither v0 -> firstFindingType [
        Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid (Core.eitherTypeLeft v0))) Nothing,
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid (Core.eitherTypeRight v0))) Nothing)]
      Core.TypeEffect v0 -> Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid v0)) Nothing
      Core.TypeForall v0 ->
        let paramName = Core.forallTypeParameter v0
        in (firstFindingType [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.typeVariableShadowingInForall")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.typeVariableShadowingInForall", f)) (Logic.ifElse (Sets.member paramName boundVars) (Just (ErrorCore.InvalidTypeErrorTypeVariableShadowingInForall (ErrorCore.TypeVariableShadowingInForallError {
            ErrorCore.typeVariableShadowingInForallErrorLocation = (Paths.SubtermPath []),
            ErrorCore.typeVariableShadowingInForallErrorName = paramName}))) Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.invalidForallParameterName")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.invalidForallParameterName", f)) (Logic.ifElse (isValidName paramName) Nothing (Just (ErrorCore.InvalidTypeErrorInvalidForallParameterName (ErrorCore.InvalidForallParameterNameError {
            ErrorCore.invalidForallParameterNameErrorLocation = (Paths.SubtermPath []),
            ErrorCore.invalidForallParameterNameErrorName = paramName}))))) Nothing)])
      Core.TypeFunction v0 -> Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid (Core.functionTypeCodomain v0))) Nothing
      Core.TypeList v0 -> Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid v0)) Nothing
      Core.TypeMap v0 ->
        let keyType = Core.mapTypeKeys v0
        in (firstFindingType [
          Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.nonComparableMapKeyType")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.nonComparableMapKeyType", f)) (case keyType of
            Core.TypeFunction _ -> Just (ErrorCore.InvalidTypeErrorNonComparableMapKeyType (ErrorCore.NonComparableMapKeyTypeError {
              ErrorCore.nonComparableMapKeyTypeErrorLocation = (Paths.SubtermPath []),
              ErrorCore.nonComparableMapKeyTypeErrorKeyType = keyType}))
            _ -> Nothing)) Nothing,
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid keyType)) Nothing),
          (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid (Core.mapTypeValues v0))) Nothing)])
      Core.TypePair v0 -> firstFindingType [
        Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid (Core.pairTypeFirst v0))) Nothing,
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid (Core.pairTypeSecond v0))) Nothing)]
      Core.TypeRecord v0 -> firstFindingType [
        Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.emptyRecordType")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.emptyRecordType", f)) (Logic.ifElse (Lists.null v0) (Just (ErrorCore.InvalidTypeErrorEmptyRecordType (ErrorCore.EmptyRecordTypeError {
          ErrorCore.emptyRecordTypeErrorLocation = (Paths.SubtermPath [])}))) Nothing)) Nothing,
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.duplicateRecordTypeFieldNames")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.duplicateRecordTypeFieldNames", f)) (checkDuplicateFieldTypes v0 (\dupName -> Just (ErrorCore.InvalidTypeErrorDuplicateRecordTypeFieldNames (ErrorCore.DuplicateRecordTypeFieldNamesError {
          ErrorCore.duplicateRecordTypeFieldNamesErrorLocation = (Paths.SubtermPath []),
          ErrorCore.duplicateRecordTypeFieldNamesErrorName = dupName}))))) Nothing),
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (firstTypeError (Lists.map (\f -> checkVoid (Core.fieldTypeType f)) v0))) Nothing)]
      Core.TypeSet v0 -> firstFindingType [
        Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.nonComparableSetElementType")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.nonComparableSetElementType", f)) (case v0 of
          Core.TypeFunction _ -> Just (ErrorCore.InvalidTypeErrorNonComparableSetElementType (ErrorCore.NonComparableSetElementTypeError {
            ErrorCore.nonComparableSetElementTypeErrorLocation = (Paths.SubtermPath []),
            ErrorCore.nonComparableSetElementTypeErrorElementType = v0}))
          _ -> Nothing)) Nothing,
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (checkVoid v0)) Nothing)]
      Core.TypeUnion v0 -> firstFindingType [
        Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.emptyUnionType")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.emptyUnionType", f)) (Logic.ifElse (Lists.null v0) (Just (ErrorCore.InvalidTypeErrorEmptyUnionType (ErrorCore.EmptyUnionTypeError {
          ErrorCore.emptyUnionTypeErrorLocation = (Paths.SubtermPath [])}))) Nothing)) Nothing,
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.singleVariantUnion")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.singleVariantUnion", f)) (Logic.ifElse (Equality.equal (Lists.length v0) 1) (Optionals.cases (Lists.maybeHead v0) Nothing (\singleField -> Just (ErrorCore.InvalidTypeErrorSingleVariantUnion (ErrorCore.SingleVariantUnionError {
          ErrorCore.singleVariantUnionErrorLocation = (Paths.SubtermPath []),
          ErrorCore.singleVariantUnionErrorFieldName = (Core.fieldTypeName singleField)})))) Nothing)) Nothing),
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.duplicateUnionTypeFieldNames")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.duplicateUnionTypeFieldNames", f)) (checkDuplicateFieldTypes v0 (\dupName -> Just (ErrorCore.InvalidTypeErrorDuplicateUnionTypeFieldNames (ErrorCore.DuplicateUnionTypeFieldNamesError {
          ErrorCore.duplicateUnionTypeFieldNamesErrorLocation = (Paths.SubtermPath []),
          ErrorCore.duplicateUnionTypeFieldNamesErrorName = dupName}))))) Nothing),
        (Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.voidInNonBottomPosition", f)) (firstTypeError (Lists.map (\f -> checkVoid (Core.fieldTypeType f)) v0))) Nothing)]
      Core.TypeVariable v0 -> Logic.ifElse (enabled p (Core.Name "hydra.error.core.InvalidTypeError.undefinedTypeVariable")) (Optionals.map (\f -> (Core.Name "hydra.error.core.InvalidTypeError.undefinedTypeVariable", f)) (Logic.ifElse (Sets.member v0 boundVars) Nothing (Just (ErrorCore.InvalidTypeErrorUndefinedTypeVariable (ErrorCore.UndefinedTypeVariableError {
        ErrorCore.undefinedTypeVariableErrorLocation = (Paths.SubtermPath []),
        ErrorCore.undefinedTypeVariableErrorName = v0}))))) Nothing
      _ -> Nothing