skip to content

When a match case's pattern fits but its guard condition evaluates to false, what does the match do next?

level: middleimportance: must knowfreq 55%

answer

  1. part of the test, not a filter
  2. pattern first, then the guard
  3. false guard means try the next case
  4. no error, no body, no rewind
  5. relates two bound parts

basics

~20 s

The case is rejected as a whole and matching continues with the cases below it, exactly as if the pattern had not fitted. A failed guard is not an error and does not abort the match.

solid answer

~40 s

A guard is part of the case's test, not a filter applied after the case is chosen. The pattern is matched first, which produces the bindings; the guard is then evaluated over those bindings; and only if it holds does the case body run. If the guard is false the case is discarded and matching resumes at the next case, so a later case can still handle the value. That is why guards buy you something patterns cannot express: the guard is an ordinary boolean expression over the names just bound, so it can relate two parts to each other - `where end == start` - or call a function. If no case is left, the match produces nothing, which is a run-time failure rather than a guard error.

code

pseudocode · 7 lines
pseudocode
match request
    case Booking(guest, Range(start, end)) where nights(start, end) > 14
        return longStayQuote(guest, start, end)
    case Booking(guest, Range(start, end))
        return nightlyQuote(guest, start, end)
    case _
        return fallbackQuote(request)

go deeper

for a junior

Remember the one-line answer: a false guard means the case is skipped and matching goes on to the next case. It is not an error and the body does not run.

for a middle

Explain the sequence - pattern, bindings, guard, body - and give a condition only a guard can express, such as one that compares two parts the pattern just bound.

for a senior

Show the operational awareness: guards run during selection, so an expensive or effectful guard costs you on values that another case ends up handling, and a condition buried in a guard is hard to reason about later.

for a principal

The trade-off you own is how much decision logic is allowed to live in guards. Conditions in guards are invisible to structural reasoning about the cases, so a house rule about what belongs in the shape is worth setting.

## A guard is part of the test, not a post-filter A case has two halves. The **pattern** decides whether the value has the right shape and, in doing so, names its parts. The **guard** is a boolean condition attached to that one case, written in terms of the names the pattern just bound. The order is fixed and worth stating precisely: 1. The pattern is matched against the value. If the shape does not fit, the guard is never evaluated at all. 2. The bindings are established. 3. The guard is evaluated over those bindings. 4. Only if the guard is true does the case body run. Step 3 happening after step 2 is the mechanical fact most candidates state backwards. A guard cannot run first, because without the bindings it has nothing to talk about. ## What a false guard does The case is rejected **as a whole**, and matching continues with the remaining cases in order. The bindings that were made are discarded with it. Concretely: - It is **not** an error. Nothing is thrown, nothing is logged, no default is substituted. - It does **not** enter the body. The body of a guarded case never runs on a value its guard rejected. - It does **not** rewind to an earlier case. Cases above it were already tried and did not fit. - If some later case fits, that case handles the value, and from the outside the result is indistinguishable from the guarded case never having been written. - If nothing below fits either, no case is selected and the match has no value to produce, which surfaces as a run-time failure. ## Why a guard, and not a bigger pattern Patterns test **structure and constants**: this shape, that variant, this exact value in that position. A great many real conditions are not structural. | Condition | Expressible as a pattern? | Natural home | |---|---|---| | The stay is a two-ended range | Yes - it is a shape | Pattern | | The stay is open-ended | Yes - it is a variant | Pattern | | The end date equals the start date | No - it relates two bound parts | Guard | | The stay is longer than fourteen nights | No - it computes over a binding | Guard | | The guest is the string `anonymous` | Yes - a constant in a position | Pattern | The boundary is simple: anything a pattern can decide by looking at the value's shape belongs in the pattern, and anything that needs the parts to be compared or computed with belongs in the guard. Putting a structural condition in the guard hides it from the reader and from whatever the compiler can reason about; putting a relational condition in the pattern is usually impossible. ## The version that cannot fall through The same condition written inside the case body behaves differently in one decisive way. Once a case is selected, the match is over: control is inside that body and the cases below it are unreachable for this value. So a body-side `if` must handle every value the pattern admits, right there, including the ones the condition rejects. A guard, by contrast, hands the value back to the match. This is the practical difference to state in an interview: - **Guard false** - try the next case. - **Body condition false** - you are already committed to this case; deal with it here. Both are legitimate. Use the guard when another case should get a chance at the value; use the body condition when this case owns the value and is merely choosing between two results. ## Ordering, overlap and cost Because a guarded case can decline a value, a broad pattern with a narrow guard placed first does **not** shadow everything below it - it shadows exactly the values its guard admits. That makes guards the usual way to write a special case above a general one. Two costs are worth knowing. - A guard is arbitrary code. It is evaluated during matching, so an expensive or effectful guard runs as part of selection, and it may run for a value that ends up handled somewhere else entirely. - A condition hidden in a guard is generally invisible to whatever checks the match for completeness, so a set of cases that a reader can see covers everything may not be provably complete. Languages differ in how loudly they say so. ## What an interviewer is listening for - "It falls through to the next case" - stated without hedging, and without calling it an error. - The **order**: pattern, bindings, guard, body. - A concrete condition that only a guard can express, ideally one relating two bound parts. - Awareness that the body-side `if` is not the same thing, because it cannot give the value back.

  • Why can a guard express a condition that the pattern itself cannot?
    A pattern decides by structure and constants: this variant, this shape, this exact value in that position. A guard is an ordinary boolean expression evaluated over the names the pattern bound, so it can compare two parts with each other or compute from them - a relation, not a shape.
  • What happens if every case's pattern fits some value but each of their guards is false?
    No case is selected, so the match has nothing to produce and fails at run time. A guard is a condition that shape-based reasoning about the cases generally cannot see through, which is why a set of cases that looks complete to a reader may still leave a value unhandled.
  • Does an expensive guard cost anything when a later case ends up handling the value?
    Yes. Guards are evaluated during selection, in order, so a costly guard runs for every value whose pattern fits it - including values it rejects and some other case handles. Keep guards cheap and free of effects, or move the work into the body of the case that wins.

saying these in an interview costs you the question

  • Thinks a failed guard aborts the whole match with an error
  • Believes the guard is evaluated before the pattern binds anything
  • Treats the guard as filtering the result after the body ran
  • Expects control to return to an earlier case when a guard fails
  • Says a body-side condition behaves the same as a guard
  • Assumes a guard can rebind or modify the value being matched