R2 — Expressions and free variables

Goal: understand why an unknown name is a perfectly good answer, learn the two commands that rearrange an expression, and meet the two places the notation will surprise you.

A name you have not defined is not an error

> y
y

run this session

y is a free variable — it stands for itself. In most languages that would be an error (“undefined”); here it is the ordinary case, because algebra is mostly about quantities whose value you do not know yet.

R1 put the rule as “a number when one exists, an expression when it does not”. Here is the same rule with both halves side by side:

> 2 * 3 + 1
7.0
> 2 * y + 1
((2.0 * y) + 1.0)

run this session

The second line did not fail. There was simply nothing left to do once y turned out to be unknown, so the answer is the expression itself.

Why so many brackets?

2 * y + 1 came back as ((2.0 * y) + 1.0) — every operation parenthesised, nothing left to precedence. That is deliberate: an answer is always valid input. You can copy any result straight back to the prompt and get the same thing again:

> ((2.0 * y) + 1.0)
((2.0 * y) + 1.0)

run this session

It is the same property that lets a whole session be saved as a text file and replayed (R9), so the brackets are worth the visual noise.

Rearranging: simplify and expand

Plain evaluation rearranges nothing — it computes what it can and stops. Two commands do the rearranging, and each does exactly one job.

simplify applies structural rules: identities, constant folding, inverse pairs.

> y + y
(y + y)
> simplify y + y
(2.0 * y)
> simplify x * 1 + 0
x
> simplify ln(exp(z))
z

run this session

expand distributes products over sums:

> expand 2 * (x + 3)
((2.0 * x) + 6.0)

run this session

Notice 2 * 3 came back unfolded. expand distributes and nothing else; folding constants is simplify’s job. Each command does one thing, and you choose which.

What simplify will not do

> simplify 2x + 3x
((2.0 * x) + (3.0 * x))

run this session

It left that alone. simplify is a single structural pass, not an algebraic normal form, so it will not gather unlike-looking terms for you. Knowing where it stops saves an argument with it later — and when you need a polynomial genuinely tidied, expand followed by simplify gets most of the way.

Trap 1 — a space between two names is load-bearing

Juxtaposition means multiplication, so a number beside a name is a product, and so are two names separated by a space:

> 2 y
(2.0 * y)
> theta phi
(theta * phi)

run this session

Close the space, though, and it is no longer two names at all:

> thetaphi
thetaphi
> simplify thetaphi - theta * phi
(thetaphi + (-1.0 * (theta * phi)))

run this session

thetaphi is one variable with a long name, and the subtraction does not cancel because the two expressions have nothing in common.

The rule is unavoidable rather than careless: a name may be several characters long, so ab has to mean something, and reading it as one name is the only choice that does not make alpha mean a*l*p*h*a. The practical form is simple — write *, or at least a space, between two names — and the failure is silent, because both readings are legitimate expressions.

Trap 2 — an unknown function name becomes a variable

Writing a function’s own name alone is an error, because the grammar has claimed it:

> sin
parse error: [1.4] failure: 'sin' is a reserved word and cannot be used as a variable

run this session

But a name the grammar has not claimed is just a variable — even when you write it like a call:

> sqrt(9)
(sqrt * 9.0)
  note: 'sqrt' is not a function here; 'sqrt(...)' parses as a product with 'sqrt'

run this session

That is sqrt * 9: the variable sqrt times nine, because there is no sqrt function in this language and juxtaposition is multiplication.

The grammar cannot refuse it, so the REPL warns instead. The note is not an error — the expression is valid and will evaluate, simplify and save perfectly well. It is there because a name written immediately before ( is almost never meant as a product, and a silent product would be a confident answer to a question you never asked.

Square roots are powers:

> 9^0.5
3.0
> 9^(1/2)
3.0

run this session

That warning fires on call syntax only. For a bare unknown name — an ordinary typo — there is a second diagnostic you can switch on, and R9 does.

Try it yourself

Predict each answer before you run it: cos, 16^0.25, a b, ab, simplify (x + 0) * 1.

Answers ```text > cos parse error: [1.4] failure: 'cos' is a reserved word and cannot be used as a variable > 16^0.25 2.0 > a b (a * b) > ab ab > simplify (x + 0) * 1 x ``` *[run this session](https://lycogrypho.github.io/Leonardo/app/#s=precision%205%0Acolors%20dark%0Apretty%20off%0Alatex%20off%0Anames%20off%0Alogic%20minmax%0Alogic%20symmetric%20off%0Aexact%20precision%2030%0Aexact%20off)* `a b` is a product of two variables; `ab` is a single variable. One space is the whole difference, and nothing warns you about it.

What you learned

  • A free variable is an ordinary answer, not a failure.
  • Answers are fully parenthesised because an answer is valid input — you can paste one back.
  • simplify applies structural rules; expand distributes. Neither is a normal form, and simplify will not collect 2x + 3x.
  • theta phi is a product; thetaphi is one variable. The space is load-bearing, and nothing warns you.
  • An unknown function name becomes a variable, and the REPL warns when it looks like a call. The radical is x^(1/2).

Previous: R1 — Five minutes · Next: R3 — Naming things · All lessons


This site uses Just the Docs, a documentation theme for Jekyll.