CH09: MUTABILITY
Refs
A ref is like a pointer or reference in an imperative language. It is a location in memory whose
contents may change. Refs are also called ref cells, the idea being that there’s a cell in
memory that can change.
let x = ref 0, creates a reference using the ref keyword. That’s a location in memory whose
contents are initialized to 0. ! is the
dereference operator in OCaml, not Boolean negation.
The third phrase, x := 1, is an assignment. It mutates the contents x to be 1. Note that x itself
still points to the same location (i.e., address) in memory. Memory is mutable; variable
bindings are not.
Aliasing
Aliasing in programming refers to a situation where two or more variables or references point
to the same memory location. When this happens, a change made to the memory location
through one reference is reflected in the others, because they are all referring to the same
underlying data.
Syntax and Semantics
References in OCaml are associated with locations in memory.
A location refers to a specific spot in memory where data is stored.
While OCaml uses these locations internally, programmers cannot directly access or
manipulate these memory addresses. Instead, they interact with abstractions like
variables, references, or functions.
This makes OCaml safer because programmers don't deal directly with raw memory, reducing
the likelihood of bugs related to memory corruption.
Syntax.
• Ref creation: ref e
• Ref assignment: e1 := e2
• Dereference: !e
Dynamic semantics.
• To evaluate ref e,
– Evaluate e to a value v
– Allocate a new location loc in memory to hold v
– Store v in loc
– Return loc
• To evaluate e1 := e2,
– Evaluate e2 to a value v, and e1 to a location loc.
– Store v in loc.
– Return (), i.e., unit.
• To evaluate !e,
– Evaluate e to a location loc.
– Return the contents of loc
Static semantics.
We have a new type constructor, ref, such that t ref is a type for any type t. Note that the ref
keyword is used in two ways: as a type constructor, and as an expression that constructs refs.
ref as type constructor: int ref, string ref, (int * int) ref
ref as expression: let r = ref 10 (* Create a reference to the value 10 *)
. • ref e : t ref if e : t.
• e1 := e2 : unit if e1 : t ref and e2 : t.
• !e : t if e : t ref
Sequencing of Effects
The semicolon operator (;) is used in OCaml to sequence operations (or effects), which is
especially relevant when dealing with mutable state, such as modifying references (ref).
• Syntax: e1; e2
• Dynamic semantics: To evaluate e1; e2,
– First evaluate e1 to a value v1.
– Then evaluate e2 to a value v2.
– Return v2. (v1 is not used at all.)
– If there are multiple expressions in a sequence, e.g., e1; e2; ...; en, then evaluate each one in
order from left to right, returning only vn.
• Static semantics: e1; e2 : t if e1 : unitande2 : t. Similarly,e1; e2; ...; en : tife1 : unit,e2 : unit,…
(i.e.,all expressions except en have type unit), and en : t.
Example: Mutable Counter
same as
incr : int ref → unit
We could even introduce our own OCaml operator for dereference. We have to put ~ infront
of it to make it parse as a prefix operator, though.
let ( ~* )= deref;;
~*p
val ( ~* ): 'a pointer → 'a
- : int = 4
Example: Recursion Without Rec
Physical Equality
Physical equality (==) compares the location in memory. But structural equality (=) the value
of variables.
SINGLY LINKED LIST
MUTABLE FIELDS
The fields of a record can be declared as mutable, meaning their contents can be updated
without constructing a new record.
• Syntax: e1 . f ← e2
•Dynamic semantics: To evaluate e1 . f <- e2, evaluate e2 to a value v2, and e1 to a value v1,
which must have a field named f. Update v1 .f to v2. Return().
• Static semantics: e1.f <- e2 : unit if e1 : t1 and t1 = {...; mutable f : t2; ...}, and e2: t2.
Refs Are Mutable Fields
Arrays and Loops
Arrays are fixed-length mutable sequences with constant - time access and update. So, they
are similar in various ways to refs, lists, and tuples. Like refs, they are mutable. Like lists, they
are (finite) sequences. Like tuples, their length is fixed in advance and cannot be resized.
Syntax.
• Array creation: [|e0; e1; ...; en|]
• Array indexing: e1.(e2)
• Array assignment: e1.(e2) <- e3
Static semantics.
• [|e0; e1; ...; en|] : t array if ei : t for all the ei.
• e1.(e2) : t if e1 : t array and e2 : int.
• e1.(e2) <- e3 : unitife1 : t arrayande2 : intande3 : t.
LOOPS
All three expressions evaluate to () after the termination of the loop. Because they always
evaluate to (), they are less general than folds, maps, or recursive functions.