and do not look the same, but they are. and
look kind of similar, but they are not. The question is equivalence -
“do these two produce the same value for every input?” It anchors the
substitution and function exercises. This
article puts the idea in one place for the equivalence and fn_equal
exercises that drill it.
The question is “every value”, not “this value”
The trap is checking one input and stopping. and agree at (both are 0) and at (both are 4). A quick check makes them look identical. They are not. At , the first is 9 and the second is
- Equivalence makes the stronger claim: the same for every value the variable can take, not the same for the values you happened to try.
That one mismatching input is a counterexample. It settles the question, because “the same for every value” dies as soon as you find one value where the expressions differ. Finding an input where they agree is not enough; almost any two expressions agree somewhere. You need to decide whether any input makes them disagree.
When they are the same: a law is hiding
Equivalent pairs have an algebraic law behind them. and follow the distributive law: multiply the through the parentheses. They are the same expression written two ways. and follow the difference of squares, a product that always expands to that particular subtraction. and are the same because “add a thing to itself” and “double a thing” name one operation.
When a pair is equivalent, do not say, “I checked a few inputs and they matched.” Say, “a law says they are the same, so they are the same for every input.” That is the difference between sampling and reasoning. It is also the line the functions practice draws in its infinite-domain exercises: many samples finding no counterexample is still not a proof.
When they differ: one input is enough
Non-equivalent pairs fail on a specific input. Find it. versus
fails at . versus agrees at (both
are 5) but fails at (10 versus 20). versus
agrees at (both 1) but fails at (4 versus 2).
The counterexample is usually small - 0, 1, 2, a negative, or any value
where a square and a double stop coinciding. Reach for the values most likely
to break the resemblance, not the ones most likely to preserve it.
This is the same recognition a programmer uses when hunting a bug: do not test
the happy path; test the input that might break the assumption. The
edge-case-reasoning thread through these exercises builds exactly that reflex,
aimed at a mathematical claim instead of a code path.
Two framings, one question
The substitution exercises ask it of expressions: “is the same as for every ?” The function exercises ask it of functions: “is the same function as ?” These are the same question in different clothes. Two functions are the same when they are the same mapping - same output for every input - and “same output for every input” is exactly “equivalent for every value.” A function is just a mapping, so function equality and expression equivalence are one idea.
The only difference is the domain. On a small finite domain, you could check every input by hand. On an infinite domain - all the integers - you cannot, so the exercise states the limit plainly: testing many samples can refute a false claim of equality but can never prove a true one. The truth-tables article makes the same distinction for logic: an exhaustive table decides a finite question; an unbounded one needs a proof, which is what the proofs and induction exercises are for.
Why this matters here
Equivalence supports three things on this site. It is the equivalence skill
in the substitution exercises, the fn_equal skill in the function exercises,
and - one layer up - the seed of
the laws hidden in your code: a law
is just an equivalence that holds for every input. That makes it safe to rely
on rather than re-check. When lambda calculus later asks whether two terms are
alpha-equivalent, it asks this same question again, about expressions that
happen to be functions.
Where to go next
-
Substitution: replace a name with a value
- the move you need before you can even ask the equivalence question.
-
The laws hidden in your code
- equivalence that holds for every input, generalized into rules you can reason with.
-
Truth tables: the logic your code already runs on
- equivalence in the logic exercises, settled exhaustively row by row.