checkEquation :: (?globals :: Globals) =>
     Ctxt TypeScheme -- context of top-level definitions
  -> Id              -- Name of the definition
  -> Equation () ()  -- Equation
  -> TypeScheme      -- Type scheme
  -> Checker (Equation () Type)

checkEquation defCtxt _ (Equation s () pats expr) tys@(Forall _ foralls constraints ty) = do
  -- Check that the lhs doesn't introduce any duplicate binders
  duplicateBinderCheck s pats

  -- Freshen the type context
  modify (\st -> st { tyVarContext = map (\(n, c) -> (n, (c, ForallQ))) foralls})

  -- Create conjunct to capture the pattern constraints
  newConjunct

  mapM_ (\ty -> do
    pred <- compileTypeConstraintToConstraint s ty
    addPredicate pred) constraints

  -- Build the binding context for the branch pattern
  st <- get
  (patternGam, tau, localVars, subst, elaborated_pats, consumptions) <-
     ctxtFromTypedPatterns s ty pats (patternConsumption st)

  -- Update the consumption information
  modify (\st -> st { patternConsumption =
                         zipWith joinConsumption consumptions (patternConsumption st) } )

  -- Create conjunct to capture the body expression constraints
  newConjunct

  -- Specialise the return type by the pattern generated substitution
  debugM "eqn" $ "### -- patternGam = " <> show patternGam
  debugM "eqn" $ "### -- localVars  = " <> show localVars
  debugM "eqn" $ "### -- tau = " <> show tau
  tau' <- substitute subst tau
  debugM "eqn" $ "### -- tau' = " <> show tau'

  patternGam <- substitute subst patternGam

  -- Check the body
  (localGam, subst', elaboratedExpr) <-
       checkExpr defCtxt patternGam Positive True tau' expr

  case checkLinearity patternGam localGam of
    [] -> do
      localGam <- substitute subst localGam

      -- Check that our consumption context approximations the binding
      ctxtApprox s localGam patternGam

      -- Conclude the implication
      concludeImplication s localVars

      -- Create elaborated equation
      subst'' <- combineSubstitutions s subst subst'
      let elab = Equation s ty elaborated_pats elaboratedExpr

      elab' <- substitute subst'' elab
      return elab'

    -- Anything that was bound in the pattern but not used up
    (p:ps) -> illLinearityMismatch s (p:|ps)

-- Polarities are used to understand when a type is
-- `expected` vs. `actual` (i.e., for error messages)
checkEquation :: (?globals :: Globals) =>
     Ctxt TypeScheme -- context of top-level definitions
  -> Id              -- Name of the definition
  -> Equation () ()  -- Equation
  -> TypeScheme      -- Type scheme
  -> Checker (Equation () Type)
checkEquation defCtxt _ (Equation s () pats expr) tys@(Forall _ foralls constraints ty) = do
  -- Check that the lhs doesn't introduce any duplicate binders
  duplicateBinderCheck s pats

  -- Freshen the type context
  modify (\st -> st { tyVarContext = map (\(n, c) -> (n, (c, ForallQ))) foralls})

  -- Create conjunct to capture the pattern constraints
  newConjunct

  mapM_ (\ty -> do
    pred <- compileTypeConstraintToConstraint s ty
    addPredicate pred) constraints

  -- Build the binding context for the branch pattern
  st <- get
  (patternGam, tau, localVars, subst, elaborated_pats, consumptions) <-
     ctxtFromTypedPatterns s ty pats (patternConsumption st)

  -- Update the consumption information
  modify (\st -> st { patternConsumption =
                         zipWith joinConsumption consumptions (patternConsumption st) } )

  -- Create conjunct to capture the body expression constraints
  newConjunct

  -- Specialise the return type by the pattern generated substitution
  debugM "eqn" $ "### -- patternGam = " <> show patternGam
  debugM "eqn" $ "### -- localVars  = " <> show localVars
  debugM "eqn" $ "### -- tau = " <> show tau
  tau' <- substitute subst tau
  debugM "eqn" $ "### -- tau' = " <> show tau'

  patternGam <- substitute subst patternGam

  -- Check the body
  (localGam, subst', elaboratedExpr) <-
       checkExpr defCtxt patternGam Positive True tau' expr

  case checkLinearity patternGam localGam of
    [] -> do
      localGam <- substitute subst localGam

      -- Check that our consumption context approximations the binding
      ctxtApprox s localGam patternGam

      -- Conclude the implication
      concludeImplication s localVars

      -- Create elaborated equation
      subst'' <- combineSubstitutions s subst subst'
      let elab = Equation s ty elaborated_pats elaboratedExpr

      elab' <- substitute subst'' elab
      return elab'

    -- Anything that was bound in the pattern but not used up
    (p:ps) -> illLinearityMismatch s (p:|ps)

-- Polarities are used to understand when a type is
-- `expected` vs. `actual` (i.e., for error messages)
checkEquation defCtxt _ (Equation s () pats expr) tys@(Forall _ foralls constraints ty) = do
  -- Check that the lhs doesn't introduce any duplicate binders
  duplicateBinderCheck s pats

  -- Freshen the type context
  modify (\st -> st { tyVarContext = map (\(n, c) -> (n, (c, ForallQ))) foralls})

  -- Create conjunct to capture the pattern constraints
  newConjunct

  mapM_ (\ty -> do
    pred <- compileTypeConstraintToConstraint s ty
    addPredicate pred) constraints

  -- Build the binding context for the branch pattern
  st <- get
  (patternGam, tau, localVars, subst, elaborated_pats, consumptions) <-
     ctxtFromTypedPatterns s ty pats (patternConsumption st)

  -- Update the consumption information
  modify (\st -> st { patternConsumption =
                         zipWith joinConsumption consumptions (patternConsumption st) } )

  -- Create conjunct to capture the body expression constraints
  newConjunct

  -- Specialise the return type by the pattern generated substitution
  debugM "eqn" $ "### -- patternGam = " <> show patternGam
  debugM "eqn" $ "### -- localVars  = " <> show localVars
  debugM "eqn" $ "### -- tau = " <> show tau
  tau' <- substitute subst tau
  debugM "eqn" $ "### -- tau' = " <> show tau'

  patternGam <- substitute subst patternGam

  -- Check the body
  (localGam, subst', elaboratedExpr) <-
       checkExpr defCtxt patternGam Positive True tau' expr

  case checkLinearity patternGam localGam of
    [] -> do
      localGam <- substitute subst localGam

      -- Check that our consumption context approximations the binding
      ctxtApprox s localGam patternGam

      -- Conclude the implication
      concludeImplication s localVars

      -- Create elaborated equation
      subst'' <- combineSubstitutions s subst subst'
      let elab = Equation s ty elaborated_pats elaboratedExpr

      elab' <- substitute subst'' elab
      return elab'

    -- Anything that was bound in the pattern but not used up
    (p:ps) -> illLinearityMismatch s (p:|ps)

-- Polarities are used to understand when a type is
-- `expected` vs. `actual` (i.e., for error messages)