checkDef :: (?globals :: Globals)
         => Ctxt TypeScheme  -- context of top-level definitions
         -> Def () ()        -- definition
         -> Checker (Def () Type)
checkDef defCtxt (Def s defName equations tys@(Forall s_t foralls constraints ty)) = do

    -- duplicate forall bindings
    case duplicates (map (sourceName . fst) foralls) of
      [] -> pure ()
      (d:ds) -> throwError $ fmap (DuplicateBindingError s_t) (d :| ds)

    -- Clean up knowledge shared between equations of a definition
    modify (\st -> st { guardPredicates = [[]]
                      , patternConsumption = initialisePatternConsumptions equations } )

    elaboratedEquations :: [Equation () Type] <- forM equations $ \equation -> do -- Checker [Maybe (Equation () Type)]
        -- Erase the solver predicate between equations
        modify' $ \st -> st
            { predicateStack = []
            , tyVarContext = []
            , guardContexts = []
            }
        elaboratedEq <- checkEquation defCtxt defName equation tys

        -- Solve the generated constraints
        checkerState <- get

        let predicate = Conj $ predicateStack checkerState
        solveConstraints predicate (getSpan equation) defName
        pure elaboratedEq

    checkGuardsForImpossibility s defName
    checkGuardsForExhaustivity s defName ty equations
    pure $ Def s defName elaboratedEquations tys
checkDef :: (?globals :: Globals)
         => Ctxt TypeScheme  -- context of top-level definitions
         -> Def () ()        -- definition
         -> Checker (Def () Type)
checkDef defCtxt (Def s defName equations tys@(Forall s_t foralls constraints ty)) = do

    -- duplicate forall bindings
    case duplicates (map (sourceName . fst) foralls) of
      [] -> pure ()
      (d:ds) -> throwError $ fmap (DuplicateBindingError s_t) (d :| ds)

    -- Clean up knowledge shared between equations of a definition
    modify (\st -> st { guardPredicates = [[]]
                      , patternConsumption = initialisePatternConsumptions equations } )

    elaboratedEquations :: [Equation () Type] <- forM equations $ \equation -> do -- Checker [Maybe (Equation () Type)]
        -- Erase the solver predicate between equations
        modify' $ \st -> st
            { predicateStack = []
            , tyVarContext = []
            , guardContexts = []
            }
        elaboratedEq <- checkEquation defCtxt defName equation tys

        -- Solve the generated constraints
        checkerState <- get

        let predicate = Conj $ predicateStack checkerState
        solveConstraints predicate (getSpan equation) defName
        pure elaboratedEq

    checkGuardsForImpossibility s defName
    checkGuardsForExhaustivity s defName ty equations
    pure $ Def s defName elaboratedEquations tys
checkDef defCtxt (Def s defName equations tys@(Forall s_t foralls constraints ty)) = do

    -- duplicate forall bindings
    case duplicates (map (sourceName . fst) foralls) of
      [] -> pure ()
      (d:ds) -> throwError $ fmap (DuplicateBindingError s_t) (d :| ds)

    -- Clean up knowledge shared between equations of a definition
    modify (\st -> st { guardPredicates = [[]]
                      , patternConsumption = initialisePatternConsumptions equations } )

    elaboratedEquations :: [Equation () Type] <- forM equations $ \equation -> do -- Checker [Maybe (Equation () Type)]
        -- Erase the solver predicate between equations
        modify' $ \st -> st
            { predicateStack = []
            , tyVarContext = []
            , guardContexts = []
            }
        elaboratedEq <- checkEquation defCtxt defName equation tys

        -- Solve the generated constraints
        checkerState <- get

        let predicate = Conj $ predicateStack checkerState
        solveConstraints predicate (getSpan equation) defName
        pure elaboratedEq

    checkGuardsForImpossibility s defName
    checkGuardsForExhaustivity s defName ty equations
    pure $ Def s defName elaboratedEquations tys