4.1 KiB
Exercise 13.3.1: I don't think you could model automatic garbage collection using single step semantics, given that it has no information about the outer evaluation context and whether a label will be used or not. We could write single step evaluation with an explicit context like this:
E ⩴ _ | t E | E v | !E | E := t | v := E | ref E
t | μ → t' | μ'
---------------------
E[t] | μ → E[t'] | μ'
and analyse for labels in E
, but I think it would be cleaner to instead have an explicit freeing operator and error value:
t ⩴ ... | free t | error
┌───────────────┐
│ t | μ → t | μ │
└───────────────┘
t | μ → t' | μ'
-------------------------
free t | μ → free t' | μ'
--------------------------------
free l | μ → unit | μ[l ↦ error]
-----------------------
error t | μ → error | μ
-----------------------
v error | μ → error | μ
-------------------------
ref error | μ → error | μ
----------------------
!error | μ → error | μ
--------------------------
error := t | μ → error | μ
--------------------------
v := error | μ → error | μ
┌───────────────┐
│ Γ | Σ ⊢ t : T │
└───────────────┘
Γ | Σ ⊢ t : Ref T
---------------------
Γ | Σ ⊢ free t : Unit
-----------------
Γ | Σ ⊢ error : T
free
ing updates the store with an error
value , so the store still never shrinks. error
s propagate upward, so if it's encountered anywhere inside a computation, the entire computation error
s. The preservation theorem remains the same, but the progress theorem now says that either a well typed term evaluates to a value or it evaluates to error
(if you don't consider error
to be a value).
Unfortunately, the type system isn't strong enough to rule out usage after free (i.e. if a term is well typed then it never errors), and I'm not sure whether it would be possible to design a type system that does. Originally, I was thinking you could remove locations from the store typing, but then it's unclear which location should be removed when typing free
, and even more unclear how to fix the preservation theorem.
Theorem (preservation): If Γ | Σ ⊢ μ
, Γ | Σ ⊢ t : T
, t | μ → t' | μ'
, then there exists Σ'
such that Γ | Σ' ⊢ μ'
, Γ | Σ' ⊢ t' : T
, and Σ ??? Σ'
.
We would want to allow, for instance, t₁ := free (ref t₂)
where t₁ ≠ t₂
, but not t₁ := free t₁
, nor (free t₁; t₁) := t₂
. All of these would shrink the store typing in some way, but it doesn't seem like there would be a way to combine the store typings from the two premises of typing assignment in a way that rules out the last two but not the first.
Exercise 13.5.8: First, suppose we had a sequencing operation t₁; t₂
, which could be primitive, or just sugar for (λ_. t₂) t₁
in our CBV language. Assuming the typing rules for the booleans and natural numbers from Chapter 8, as well as a mult
iplication function, here is a factorial function:
(λn: Nat.
(λf: Ref (Nat → Nat).
λg: Ref (Nat → Nat).
f := λx: Nat.
if (iszero x)
then (succ 0)
else (mult x (!g (pred x)));
g := !f;
!g n)
(ref (λx: Nat. x))
(ref (λx: Nat. x)))
Given some concrete n
, after evaluating the two initial references, this steps to the following:
lf ↦ λx: Nat. x
lg ↦ λx: Nat. x
lf := λx: Nat.
if (iszero x)
then (succ 0)
else (mult x (!lg (pred x)));
lg := !lf;
!lg n
After one step, we update lf
:
lf ↦ λx: Nat.
if (iszero x)
then (succ 0)
else (mult x (!lg (pred x)))
lg ↦ λx: Nat. x
lg := !lf;
!lg n
After another step, we update lg
:
lf ↦ λx: Nat.
if (iszero x)
then (succ 0)
else (mult x (!lg (pred x)))
lg ↦ λx: Nat.
if (iszero x)
then (succ 0)
else (mult x (!lg (pred x)))
!lg n
Then finally yield the factorial function we expect (dropping lf
now that we no longer need it):
lg ↦ λx: Nat.
if (iszero x)
then (succ 0)
else (mult x (!lg (pred x)))
if (iszero n)
then (succ 0)
else (mult n (!lg (pred n)))