Computation

We can store booleans and track state. Now we want to transform state. What tools do we have to compute new values?

Every type has tools for computation. For numbers, we have arithmetic. For booleans, we have logical operators. This section covers the tools that come built-in for working with boolean values.

Computation Transforms State

In the previous section, we bound values directly:

bool x = true;

The value true came from us, typed literally into the code. But what if we want to compute a new value from existing bindings?

bool a = true;
bool b = !a;

Line 1: bind true to a.

Line 2: evaluate a (get true), compute its negation (get false), bind false to b.

State table:

after lineab
1true
2truefalse

The ! symbol is an operator that took a value and transformed it, and this transformation is computation.

Here’s the key insight: computation reads state (evaluates bindings), transforms values, and the result can be bound to update state. The pattern is always the same: evaluate, transform, bind.

Expressions

Before we explore operators, we need one more definition.

An expression is code that evaluates to a value.

We’ve already seen expressions without naming them. When we wrote bool copy_of_x = x;, the x on the right side is an expression. It evaluates to whatever value x holds. A variable by itself is an expression of its type, so if x is a boolean variable, then x is a boolean expression.

A literal value like true is also an expression. It evaluates to itself.

And when we write !a, that’s an expression too. It evaluates a, applies the NOT operator, and produces a new value.

Expressions can be simple (a single variable or value) or compound (operators combining multiple parts), but they all share one thing: they evaluate to a value.

The right side of = is always an expression. We evaluate it completely, then bind the result.

Logical NOT

Let’s examine our first operator.

NOT (!) is a unary boolean operator with type signature bool → bool. It returns false when given true, and true when given false.

NOT takes a boolean and produces a boolean. It transforms true to false, and false to true. We call it negation.

Unary means “one,” so NOT takes one input.

We can describe NOT’s behavior completely with a truth table:

x!x
truefalse
falsetrue

Two rows cover every possible input, and each produces a distinct output. This is the power of booleans: we can enumerate everything.

The translation: !x reads as “not x.”

Let’s see NOT in context:

bool flag = true;
bool opposite = !flag;

Translation for line 2: “Create a boolean variable named opposite and bind the result of evaluating not flag to it.”

State table:

after lineflagopposite
1true
2truefalse

Line 2 does three things:

  1. Evaluate flag → true
  2. Apply NOT to true → false
  3. Bind false to opposite

Try it yourself.

Translate this code to English:

bool result = !flag;

Write your answer before revealing ours.

Reveal answer

“Create a boolean variable named result and bind the result of evaluating not flag to it.”

If your answer differed, note what you missed before continuing.


Try it yourself.

Write C# code for this description:

“Create a boolean variable named flipped and bind the negation of done to it.”

Reveal answer
bool flipped = !done;

If your answer differed, note what you missed before continuing.


Logical AND

What if we want to check whether two things are both true?

AND (&&) is a binary boolean operator with type signature (bool, bool) → bool. It returns true only when both inputs are true. Otherwise, it returns false.

Binary means “two,” so AND takes two inputs.

Truth table:

aba && b
falsefalsefalse
falsetruefalse
truefalsefalse
truetruetrue

Four combinations produce four outputs, and only the last row produces true.

The translation: a && b reads as “a and b.”

Let’s trace AND in action:

bool left = true;
bool right = false;
bool both = left && right;

Translation for line 3: “Create a boolean variable named both and bind the result of evaluating left and right to it.”

State table:

after lineleftrightboth
1true
2truefalse
3truefalsefalse

Line 3 does four things:

  1. Evaluate left → true
  2. Evaluate right → false
  3. Apply AND to true and false → false
  4. Bind false to both

Try it yourself.

Translate this code to English:

bool ready = loaded && valid;
Reveal answer

“Create a boolean variable named ready and bind the result of evaluating loaded and valid to it.”

If your answer differed, note what you missed before continuing.


Try it yourself.

Write C# code for this description:

“Create a boolean variable named confirmed and bind the result of active and enabled to it.”

Reveal answer
bool confirmed = active && enabled;

If your answer differed, note what you missed before continuing.


Logical OR

What if we want to check whether at least one of two things is true?

OR (||) is a binary boolean operator with type signature (bool, bool) → bool. It returns true when either input is true, or when both are true. It returns false only when both inputs are false.

Truth table:

aba || b
falsefalsefalse
falsetruetrue
truefalsetrue
truetruetrue

Only the first row produces false. Any true input makes the whole expression true.

The translation: a || b reads as “a or b.”

Let’s trace OR:

bool first = false;
bool second = true;
bool either = first || second;

State table:

after linefirstsecondeither
1false
2falsetrue
3falsetruetrue

Line 3 does four things:

  1. Evaluate first → false
  2. Evaluate second → true
  3. Apply OR to false and true → true
  4. Bind true to either

Try it yourself.

Translate this code to English:

bool allowed = admin || owner;
Reveal answer

“Create a boolean variable named allowed and bind the result of evaluating admin or owner to it.”

If your answer differed, note what you missed before continuing.


Try it yourself.

Write C# code for this description:

“Create a boolean variable named canProceed and bind the result of ready or override to it.”

Reveal answer
bool canProceed = ready || override;

If your answer differed, note what you missed before continuing.


Equality Operators

Sometimes we need to check whether two values are the same.

Equals (==) is a binary operator with type signature (bool, bool) → bool. It returns true when both inputs have the same value. Otherwise, it returns false.

Not Equals (!=) is a binary operator with type signature (bool, bool) → bool. It returns true when the inputs have different values. Otherwise, it returns false.

Truth table for ==:

aba == b
falsefalsetrue
falsetruefalse
truefalsefalse
truetruetrue

Equal values produce true. Different values produce false.

Truth table for !=:

aba != b
falsefalsefalse
falsetruetrue
truefalsetrue
truetruefalse

This is the exact opposite of ==. Different values produce true.

The translation: a == b reads as “a equals b.” a != b reads as “a does not equal b.”

Note: equality operators work for other types too, not just booleans. We’ll revisit them in each chapter as we introduce new types.


Try it yourself.

Translate this code to English:

bool same = x == y;
Reveal answer

“Create a boolean variable named same and bind the result of evaluating x equals y to it.”

If your answer differed, note what you missed before continuing.


Try it yourself.

Write C# code for this description:

“Create a boolean variable named different and bind the result of comparing whether expected does not equal actual to it.”

Reveal answer
bool different = expected != actual;

If your answer differed, note what you missed before continuing.


Compound Expressions

We can chain operators to build larger expressions.

Consider:

bool result = true && false || true;

This expression has two operators. Which one applies first?

Like arithmetic has order of operations (multiplication before addition), boolean operators have precedence:

  1. NOT (!): highest priority, applies first
  2. AND (&&): middle priority
  3. OR (||): lowest priority, applies last

Think of it this way: NOT is like a negative sign, AND is like multiplication, OR is like addition. The precedence matches.

So true || false && false means true || (false && false), not (true || false) && false:

  1. First, apply AND: false && falsefalse
  2. Then, apply OR: true || falsetrue

The result is true.

If we had read it the other way, as (true || false) && false:

  1. First, apply OR: true || falsetrue
  2. Then, apply AND: true && falsefalse

We’d get false, the wrong answer. Precedence matters.

Let’s trace a more complex expression:

!true || false && true

Step by step:

  1. Apply NOT first: !truefalse
  2. Now we have: false || false && true
  3. Apply AND next: false && truefalse
  4. Now we have: false || false
  5. Apply OR last: false || falsefalse

The entire expression evaluates to false.

Parentheses override precedence. Whatever is inside parentheses evaluates first:

!(true || false) && true

Step by step:

  1. Evaluate inside parentheses: true || falsetrue
  2. Apply NOT: !truefalse
  3. Apply AND: false && truefalse

Without parentheses, !true || false && true would evaluate differently (as we showed above). Parentheses let you control the order explicitly.

When in doubt, use parentheses. They make your intent clear to both the computer and anyone reading your code.


Try it yourself.

Evaluate this expression step by step:

true || false && false
Reveal answer
  1. AND has higher precedence than OR, so: false && falsefalse
  2. Now we have: true || false
  3. Apply OR: true || falsetrue

Result: true

If your answer differed, note what you missed before continuing.


Try it yourself.

Evaluate this expression step by step:

!false && !true || false
Reveal answer
  1. Apply NOT operators first: !falsetrue, !truefalse
  2. Now we have: true && false || false
  3. Apply AND: true && falsefalse
  4. Now we have: false || false
  5. Apply OR: false || falsefalse

Result: false

If your answer differed, note what you missed before continuing.


Short-Circuit Evaluation

What if evaluating part of an expression is expensive, or what if it could cause an error? C# has a feature that helps: it doesn’t always evaluate both sides of AND and OR.

Consider:

bool result = false && something;

AND returns true only when both sides are true. If the left side is false, the result is false no matter what the right side is. So C# doesn’t bother evaluating the right side. It already knows the answer.

This is called short-circuit evaluation.

The rules:

  • false && anythingfalse (right side never evaluated)
  • true || anythingtrue (right side never evaluated)

For AND: if the left side is false, stop. The answer is false.

For OR: if the left side is true, stop. The answer is true.

Why does this matter? Later, when we learn about functions that have side effects, short-circuit evaluation becomes important for correctness, not just efficiency. We’ll revisit this.


Try it yourself.

In the following expression, which parts actually get evaluated?

bool result = true || (false && true);
Reveal answer

Only true on the left side of || gets evaluated.

Because the left side of OR is true, the entire OR expression is true. C# never evaluates (false && true).

If your answer differed, note what you missed before continuing.


Review

Before continuing, test yourself on what you’ve learned. Attempt each exercise from memory, then search the chapter to check your answers.

Part 1: Definitions

Write the definitions from memory, then find them in the section to check.

  1. What is an expression?
  2. What is NOT (!)? Include its type signature.
  3. What is AND (&&)? Include its type signature.
  4. What is OR (||)? Include its type signature.
  5. What is Equals (==)? Include its type signature.

If any of your answers differed from the definitions in this section, note what you missed and write the corrected version.

Part 2: Truth Tables

Complete these truth tables from memory:

NOT:

x!x
true
false

AND:

aba && b
falsefalse
falsetrue
truefalse
truetrue

OR:

aba || b
falsefalse
falsetrue
truefalse
truetrue

Check your tables against the ones in this section.

Part 3: Translations

Translate each line of code to English.

  1. bool inverted = !original;
  2. bool both = first && second;
  3. bool any = x || y || z;
  4. bool match = input == expected;

Check your translations against the patterns in this section.

Part 4: Evaluate Expressions

Evaluate each expression step by step. Show your work.

  1. !false || true
  2. true && false || true && true
  3. !(true && false) || false
  4. false || !false && true

Work through each one, then trace through using the precedence rules to verify.

Part 5: Short-Circuit

For each expression, identify which parts get evaluated.

  1. false && (true || false)
  2. true || (false && true)
  3. true && false || true

Think carefully about when evaluation stops.


You now know how to transform boolean values through computation. You can negate, combine, compare, and build compound expressions.