Skip to content

Step 1 of 3 · Reading · ~2 min

Short-Circuit Evaluation

Control Flow & Functions

Logical Operators (and, or)

and and or look like binary operators — left and right, left or right — but they cannot be implemented as ordinary Binary expressions, because ordinary binary expressions always evaluate both operands before combining them. Logical operators must short-circuit: false and expensiveCall() must never call expensiveCall().

Why they need their own AST node

If you evaluated both sides eagerly, you'd break not just performance but correctness — a guard like list != nil and list.first() would crash on a nil list, defeating the entire point of writing the guard. So logical expressions get their own node, separate from Binary:

python

Parse it at its own precedence level (or binds looser than and, which binds looser than equality), but structurally it looks just like parsing any other left-associative binary operator — the difference is entirely in evaluation, not parsing.

Evaluation returns a value, not a boolean

This is the detail learners most often get wrong: and/or in a Lox-like language don't coerce their result to true/false. They return whichever operand determined the outcome:

python

Trace through the required examples:

  • true and false → left true is truthy, so for and we fall through and evaluate/return rightfalse.
  • false or true → left false is falsy, so we fall through and return righttrue.
  • 1 or 2 → left 1 is truthy, or short-circuits and returns 1 directly — 2 is never evaluated.
  • nil or "yes" → left nil is falsy, so or evaluates and returns right"yes".

Why this matters architecturally

This "return the deciding operand" behavior is what lets or double as a default-value idiom (config.timeout or 30) and what makes and useful as a guarded-access idiom — both extremely common patterns once you get to writing real programs in your language. It also means your truthiness rule (from the previous lesson) has to be applied consistently at every one of these decision points: _truthy is called on left, but the value returned is never coerced.

Edge cases to watch

  • Nested logical expressions: a or b and c must respect precedence (and tighter than or), so parse this as a or (b and c).
  • Short-circuiting must be visible through side effects: a test harness might check that a function passed as the right operand is never called when the left operand already determines the result.
Up nextFunction Declaration & CallsControl Flow & Functions

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…