-
Notifications
You must be signed in to change notification settings - Fork 15
Types and Programming Languages Chapter 13 References
We began the meeting by accidentally having a stand-up around our collected snacks and beverages (featuring a brand new smorgasbord of soft drinks provided by Geckoboard), discussing our recent trip to Bletchley Park and Paul's adventures with Net::HTTP
.
Tom kicked off the meeting by stressing that, while the chapter introduces a lot of new notation in the evaluation and typing rules, the underlying concepts are not wildly more complicated than what we've seen so far.
Paul took to the whiteboard to write up some examples of the new syntax introduced for references:
ref 0
x := 2
!x
These examples highlight the following new features in our language:
ref t
This allocates a reference, providing an initial value t
for the new cell.
t1 := t2
Assign a value t2
to a reference t1
, changing its stored value.
!t
Read the current value of the cell referenced by t
(e.g. dereferencing ref 0
would return 0
).
Our first point of confusion is that the book uses the following example:
x = ref 0
But x =
is not part of our language, it is instead meant to represent some sort of imaginary type-checking REPL but this escaped a lot of us.
We thought it would be helpful to walk through some example programs using these new constructs and step through their evaluation.
Tom took to the board to step through the following example which makes use of let
(as seen in Simple Extensions), ref t
and !t
:
let x = ref 0 in !x
Intuitively, we expect this program to evaluate to 0 as we're allocating a reference to a cell populated with 0 with ref 0
, binding that reference to the name x
and then dereferencing x
to get its value 0.
More formally, we used the evaluation rules in the chapter to evaluate the source program a single step at a time by first evaluating the let
binding of x
until it was a value.
This meant taking ref 0
and applying the evaluation rule E-RefV
(as 0 is already a value) which returns a "location". But what is a location?
Tom decided to represent locations as colours which avoided confusion with other terms (e.g. numbers) and helped express another important point: locations may be produced as we evaluate step-by-step but they cannot be entered directly by a programmer in the original program.
So we could now replace ref 0
with a location represented by the colour red:
let x =in !x
As well as doing this, we needed to represent some "store" which now contains the value of the reference: in this case, 0.
↦ 0
With that done, we could now evaluate one more step: evaluating the let
by replacing every mention of x
in the body with its value:
!![]()
(The store remains the same with
.) ↦ 0
Finally, we can evaluate the dereference to get our final value:
0
And the store? Well, that remains unchanged and still contains
. ↦ 0
We walked through another program, this time with an assignment in it:
let x = ref 0 in x := succ !x; !x
Evaluating this a step at a time, we first replace ref 0
with a new location (again, represented by the colour red) and add 0
to the store (using μ to describe the current state of the store):
let x =in x := succ !x; !x μ =
↦ 0
We can then evaluate the let
, replacing every occurrence of x
with the location:
:= succ !
; !
μ =
↦ 0
We now have a sequence and need to do the following:
- Evaluate the left-hand side until it is
unit
; - Evaluate the assignment until its right-hand side is a value;
- Evaluate
succ
until its argument is a value; - Evaluate
!
until it is a value.
Starting with the last point, dereferencing in
succ !
gives us:
:= succ 0; !
μ =
↦ 0
Now the argument to succ
is a value, the right-hand side of the assignment is also itself a value so we can change the value of in the store and the entire assignment is evaluated to
unit
:
unit; !μ =
↦ succ 0
The evaluation rules for a sequence mean that we can now discard the first term, leaving the second:
!μ =
↦ succ 0
This leaves us with one final dereference to do:
succ 0 μ =↦ succ 0
And we're done!
Confident that we understood how reference allocation, assignment and dereferencing evaluates, we decided to move onto the types of these operations and how we might use types to detect errors in programs using these primitives.
We began by discussing the kinds of problems we might be able to prevent if we could type references and their related operations:
- Assigning to something other than a reference (e.g.
true := 0
); - Dereferencing a non-reference (e.g.
!true
); - Passing a reference with the wrong type to an abstraction that requires a reference of a specific type (e.g.
(λx:Ref Nat . x) (ref true)
).
We decided to try to infer the appropriate typing rules for references by taking an example well-typed program and type checking it:
(λx:Ref Nat . x := succ !x; !x) (ref 0)
We began by looking at ref 0
and asking what the type of this term would be:
Γ ⊢ 0 : Nat
-------------------
Γ ⊢ ref 0 : Ref Nat
We decided that it should have the type Ref Nat
and we could check this by looking at the type of its value (in this case Nat
). More generally, this gave us a generic typing rule for ref t
:
Γ ⊢ t : T
-------------------
Γ ⊢ ref t : Ref T
For assignment, we similarly derived the following:
Γ ⊢ x : Ref Nat 0 : Nat
--------------------------
x := 0 : Unit
Which gave us the generic typing rule for t := t
:
Γ ⊢ t1 : Ref T t2 : T
------------------------
t1 := t2 : Unit
Similarly, for dereferencing (which is sort of the opposite of our typing rule for allocating a reference):
Γ ⊢ t : Ref T
-------------
Γ ⊢ !t : T
We justified the fact that references had to have their own type constructor Ref T
much like we've seen with functions (T → T
) and lists as we need to preserve both the fact that they are references (and not, say, Nat
directly) in order to type check dereferencing and assignment and preserve the type of their value so that we can type check a term after dereferencing.
While we were pretty pleased with our very own typing rules, it turns out that the chapter still had a treat for us in store: the concept of store typing.
Evaluating and even type checking our example programs hadn't led us to explicitly keep track of the types of references but, in order to prove the safety of the type system, we need to be able to type check a term like the following:
!![]()
But we can't possibly do that if we don't know what the type of is.
Enter store typing (denoted by Σ)!
While we've had typing contexts (denoted by Γ) populated by variable declarations in our programs previously, we now have a second store of types.
This means that our typing rules need to also take this into account, e.g.
Γ | Σ ⊢ t : T
---------------------
Γ | Σ ⊢ ref t : Ref T
The unusual thing here is that, unlike the typing context Γ, none of our rules ever populate the store typing. Instead, it is a side-effect of evaluation.
James explained "aliasing" by contrasting the following two programs:
{ref 0, ref 0}
(λx. {x, x}) ref 0
The first would allocate two separate references, evaluating to:
{,
}
Whereas the second would allocate a single reference but return a pair with two instances of this same reference (these being aliases):
{,
}
- We continue to enjoy TAPL with some pleasant surprise how far we've made through what is really a postgraduate type theory textbook
- The meetings (and Tom's leadership) continue to be invaluable as a few people agreed that they would not be able to understand the material without the club
- The variety of soft drinks were a great new addition
- There was some discussion about the ownership of Attending.io pages (with each organiser using their own account meaning they are the only one who can make edits) but no one felt strong enough to make a change (e.g. create a single unified club account)
Paul rambled on about how expectations/preconceptions seem to have a huge impact on software development job satisfaction, how to coach junior developers and was dismayed to be called out by Leo and Chris when he started telling a fascinating work-related anecdote about office reception signage only to discover he'd told it before.
Thanks to Leo and Geckoboard for hosting and providing beverages (both soft and not), Laura for organising the meeting and bringing bread and dips, to Tom for leading the meeting, to James for some valiant whiteboarding, to Chris for yet more delicious bread, Charlie and all those who contributed snacks.
- Home
- Documentation
- Choosing a Topic
- Shows & Tells
- Miscellaneous
- Opt Art
- Reinforcement Learning: An Introduction
- 10 Technical Papers Every Programmer Should Read (At Least Twice)
- 7 More Languages in 7 Weeks
- Lua, Day 1: The Call to Adventure
- Lua, Day 2: Tables All the Way Down
- Lua, Day 3
- Factor, Day 1: Stack On, Stack Off
- Factor, Day 2: Painting the Fence
- Factor, Day 3: Balancing on a Boat
- Elm, Day 1: Handling the Basics
- Elm, Day 2: The Elm Architecture
- Elm, Day 3: The Elm Architecture
- Elixir, Day 1: Laying a Great Foundation
- Elixir, Day 2: Controlling Mutations
- Elixir, Day 3: Spawning and Respawning
- Julia, Day 1: Resistance Is Futile
- Julia, Day 2: Getting Assimilated
- Julia, Day 3: Become One With Julia
- Minikanren, Days 1-3
- Minikanren, Einstein's Puzzle
- Idris Days 1-2
- Types and Programming Languages
- Chapter 1: Introduction
- Chapter 2: Mathematical Preliminaries
- Chapter 3: Untyped Arithmetic Expressions
- Chapter 4: An ML Implementation of Arithmetic Expressions
- Chapter 5: The Untyped Lambda-Calculus
- Chapters 6 & 7: De Bruijn Indices and an ML Implementation of the Lambda-Calculus
- Chapter 8: Typed Arithmetic Expressions
- Chapter 9: The Simply-Typed Lambda Calculus
- Chapter 10: An ML Implementation of Simple Types
- Chapter 11: Simple Extensions
- Chapter 11 Redux: Simple Extensions
- Chapter 13: References
- Chapter 14: Exceptions
- Chapter 15: Subtyping – Part 1
- Chapter 15: Subtyping – Part 2
- Chapter 16: The Metatheory of Subtyping
- Chapter 16: Implementation
- Chapter 18: Case Study: Imperative Objects
- Chapter 19: Case Study: Featherweight Java
- The New Turing Omnibus
- Errata
- Chapter 11: Search Trees
- Chapter 8: Random Numbers
- Chapter 35: Sequential Sorting
- Chapter 58: Predicate Calculus
- Chapter 27: Perceptrons
- Chapter 9: Mathematical Research
- Chapter 16: Genetic Algorithms
- Chapter 37: Public Key Cryptography
- Chapter 6: Game Trees
- Chapter 5: Gödel's Theorem
- Chapter 34: Satisfiability (also featuring: Sentient)
- Chapter 44: Cellular Automata
- Chapter 47: Storing Images
- Chapter 12: Error-Correcting Codes
- Chapter 32: The Fast Fourier Transform
- Chapter 36: Neural Networks That Learn
- Chapter 41: NP-Completeness
- Chapter 55: Iteration and Recursion
- Chapter 19: Computer Vision
- Chapter 61: Searching Strings
- Chapter 66: Church's Thesis
- Chapter 52: Text Compression
- Chapter 22: Minimum spanning tree
- Chapter 64: Logic Programming
- Chapter 60: Computer Viruses
- Show & Tell
- Elements of Computing Systems
- Archived pages