Skip to content
academia.sh

Lesson 02 / 18

Variables and Binding

How a name is bound to a value, the meaning of assignment, two models of variables, and naming conventions.

Contents

The previous lesson wrote measurement_1 = 12 and said that it stores a value. This lesson fills in that statement: how is the bond between a name and a value established, when does it change, and what does it correspond to in memory?

The question runs deeper than it looks, because languages model this bond in two different ways, and the difference between them produces observable consequences in later lessons.

Assignment Is a Binding Operation

The statement measurement_1 = 12 executes in two steps:

  1. The expression on the right of the equals sign is evaluated; a value is produced.
  2. That value is bound to the name on the left.

Order matters. The statement total = total + 5 looks like a meaningless equation in mathematics; as a program it is clear: the expression on the right is computed first (adding 55 to the old value), and then the result is bound to the same name again.

For this reason the equals sign here means “bind,” not “equals.” Some languages use a separate symbol such as := to avoid the confusion; the equality test, in turn, is usually written ==.

What a Variable Holds: Two Models

Languages model a variable in two different ways. The difference becomes clear when considered together with the memory model established in the previous course.

The memory-cell model. A variable is a named region of memory reserved for a particular type. Assignment changes that region’s contents. The variable’s address is fixed for its entire lifetime; what changes is the bit pattern inside it. Most low-level languages use this model.

The name-object binding model. Values sit in memory as independent objects; a variable is a label attached to one of those objects. Assignment does not change the object’s content; it moves the label to a different object. Multiple labels can be attached to the same object.

The observable difference between the two models shows up in whether two variables share the same data:

a = [1, 2, 3]      # a list object; 'a' is bound to it
b = a              # 'b' is bound to the same object; no copy
b.append(4)

print(a)           # [1, 2, 3, 4]  — also visible through 'a'
print(a is b)      # True          — same object
print(a == b)      # True          — same content

c = [1, 2, 3, 4]
print(a is c)      # False         — different objects
print(a == c)      # True          — same content

The is operator asks about identity: are two names bound to the same object? The == operator asks about equality: is their content the same? Confusing the two questions is a common source of error in programs that work with shared data.

This distinction will come up again in the lesson on passing arguments to functions; the “by value or by reference” discussion there is a direct consequence of the two models here.

The way to break sharing is to produce an explicit copy. Copying itself has two depths: a shallow copy, which copies only the outer shell, and a deep copy, which copies the entire nested structure. In a shallow copy the inner objects are still shared; this is a common reason data assumed to be copied changes unexpectedly. Since the cost of copying grows with the size of the data, the choice between sharing and copying is a design decision.

Rebinding and Order

A name can be bound to different values over the course of a program. This brings with it an order-related trap: swapping the values of two variables cannot be done with two direct assignments.

x = 3
y = 7

# Wrong: x's old value is lost after the first assignment.
x = y
y = x
print(x, y)        # 7 7  — no swap happened

# Correct: a temporary name is used.
x, y = 3, 7
temp = x
x = y
y = temp
print(x, y)        # 7 3

Some languages support multiple assignment, and the swap collapses to a single line (x, y = y, x); since the right side is fully evaluated before binding takes place in this form, no temporary name is needed. This syntactic convenience does not change the underlying rule.

An Undefined Name

What happens if a name is accessed before it is bound? The answer depends on the language’s variable model, and this is the second observable difference between the two models.

In the name-object binding model, the name is not yet bound to any object; accessing it gives an explicit error:

# print(total)     -> runtime error: 'total' is not defined
total = 0          # bound first
print(total)       # 0

In the memory-cell model, space has already been reserved for the variable, and a bit pattern already sits there. If the language does not zero out the reserved space, the value read is a meaningless leftover pattern from previous use. The program may or may not crash; it can give a different result on every run.

The danger of this second case is that the error has no symptom. For this reason, languages that use this model either require an initial value, produce a compiler warning, or automatically zero out the reserved space.

The general rule is the same under both models: a variable is bound to a meaningful value before it is used. Putting the initial assignment on the same line as the declaration makes this rule visible.

Naming

Names follow the language’s rules and the team’s conventions.

Rules belong to the language and are mandatory: which characters a name can start with, whether case is distinguished, which words are reserved. Keywords such as if or while cannot be used as variable names.

Conventions belong to the team and exist for readability:

  • A name states what the variable holds: total, measurement_count, largest.
  • Single-letter names are used only in narrow, conventional contexts (such as a loop counter).
  • An abbreviation is spelled out if it cannot be assumed familiar to the reader.
  • A single naming convention is used within a project; the same concept is not referred to by two different names.

Choosing names is not decoration. Code is read more often than it is written; names determine the effort every reader of the code spends. The software design curriculum details this topic under the heading of clean code.

Constants

Some names must stay bound to the same value for the entire run of the program: a conversion factor, an upper bound, a configuration value. These are called constants.

Languages handle this in two ways. Some languages enforce constancy: reassigning a constant is a compile error. Others rely on convention alone — the name is written in uppercase, and it is expected not to be changed.

MEASUREMENT_LIMIT = 100        # treated as constant by convention

The benefit of using a constant is that it prevents the same value from being repeated in multiple places in the code. When the value changes, a single line is updated; numbers scattered through the code do not need to be hunted down. Its second, less obvious benefit is loading the name with meaning: the number 100 appearing in a condition tells the reader nothing, while the name MEASUREMENT_LIMIT states what the bound represents.

Summary

  • Assignment evaluates the expression on the right first, then binds the result to the name on the left; the equals sign means binding, not equality.
  • Languages model a variable in two ways: a named memory cell, or a label attached to an object.
  • Multiple names can be bound to the same object; is tests identity, == tests content equality.
  • The order of rebinding matters; a swap requires a temporary name or multiple assignment.
  • Naming rules belong to the language, conventions belong to the team; a name should state what the variable holds.
  • Constants gather repeated values into a single point.

Next Step

This lesson bound names to numbers and to lists, and the two were seen to behave differently. What exactly does a value’s type determine, though, and how many types are there? The next lesson takes up the basic data types and the operations each of them supports.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close