22-1-midterm
22-1-midterm
Instructions
• When you write expressions in your answers, you can freely choose between concrete syntax and
abstract syntax to write the expressions. For example, both { x => x } and λx.x are possible.
• Pages 2 and 3 show the definitions of the languages defined in the lectures. If you need the definitions
while solving the questions, refer to them.
1
Concrete syntax
[VAE]
expr ::= num | "(" expr "+" expr ")" | "(" expr "-" expr ")" | "(" expr "*" expr ")"
| "if0" "(" expr ")" "{" expr "}" "else" "{" expr "}" | "{" expr ";" expr "}"
| id | "{" "val" id "=" expr ";" expr "}"
[FAE, LFAE]
expr ::= ... | "{" id "=>" expr "}" | expr "(" expr ")"
[RFAE]
expr ::= ... | "{" "def" id "(" id ")" "=" expr ";" expr "}"
[BFAE]
expr ::= ... | "Box" "(" expr ")" | expr "." "get" | expr "." "set" "(" expr ")"
[MFAE]
expr ::= ... | "{" id "=" expr "}"
Abstract syntax
Each language uses only a part of the below definition, according to the concrete syntax defined above.
e ::= n | e + e | e − e | e × e | if0 e e e | e; e | x | val x = e; e
| λx.e | e e | def x(x) = e; e | ref e | !e | e := e | x = e
sealed trait Expr
case class Num(n: Int) extends Expr
case class Add(l: Expr, r: Expr) extends Expr
case class Sub(l: Expr, r: Expr) extends Expr
case class Mul(l: Expr, r: Expr) extends Expr
case class If0(c: Expr, t: Expr, f: Expr) extends Expr
case class Seq(l: Expr, r: Expr) extends Expr
case class Id(x: String) extends Expr
case class Val(x: String, e: Expr, b: Expr) extends Expr
case class Fun(x: String, b: Expr) extends Expr
case class App(f: Expr, a: Expr) extends Expr
case class Rec(f: String, x: String, b: Expr, e: Expr) extends Expr
case class NewBox(e: Expr) extends Expr
case class OpenBox(b: Expr) extends Expr
case class SetBox(b: Expr, e: Expr) extends Expr
case class Set(x: String, e: Expr) extends Expr
Values
[VAE]
v ::= n
sealed trait Value
case class NumV(n: Int) extends Value
[FAE, RFAE]
v ::= · · · | hλx.e, σi
case class CloV(x: String, b: Expr, var env: Env) extends Value
[BFAE, MFAE]
v ::= · · · | a
case class AddrV(a: Addr) extends Value
[LFAE]
v ::= · · · | (e, σ)
case class ExprV(e: Expr, env: Env) extends Value
2
Semantics
[VAE, FAE, RFAE] (VAE and FAE use only a part of the below rules.)
fin
σ ∈ Id →7 Val
type Env = Map[String, Value]
σ ` e1 ⇒ n1 σ ` e2 ⇒ n2 σ ` e1 ⇒ n1 σ ` e2 ⇒ n2 σ ` e1 ⇒ n1 σ ` e2 ⇒ n2
σ`n⇒n
σ ` e1 + e2 ⇒ n1 + n2 σ ` e1 − e2 ⇒ n1 − n2 σ ` e1 × e2 ⇒ n1 × n2
σ ` e1 ⇒ 0 σ ` e 2 ⇒ v2 σ ` e 1 ⇒ v1 v1 6= 0 σ ` e 3 ⇒ v3 σ ` e 1 ⇒ v1 σ ` e 2 ⇒ v2
σ ` if0 e1 e2 e3 ⇒ v2 σ ` if0 e1 e2 e3 ⇒ v3 σ ` e 1 ; e 2 ⇒ v2
x ∈ Domain(σ) σ ` e 1 ⇒ v1 σ[x 7→ v1 ] ` e2 ⇒ v2
σ ` λx.e ⇒ hλx.e, σi
σ ` x ⇒ σ(x) σ ` val x = e1 ; e2 ⇒ v2
σ, M ` e1 ⇒ a, M1 σ, M1 ` e2 ⇒ v, M2
σ, M ` e1 := e2 ⇒ v, M2 [a 7→ v]
σ, M ` e1 ⇒ hλx.e, σ 0 i, M1
0
σ, M1 ` e2 ⇒ v , M2 a 6∈ Domain(M2 ) σ 0 [x 7→ a], M2 [a 7→ v 0 ] ` e ⇒ v, M3
σ, M ` e1 e2 ⇒ v, M3
3
1) (5pts) The following paragraph explains basic concepts in programming languages:
(a) defines how expressions look like, and (b) defines what expressions evaluate
to. There are two sorts of (a) : (c) , which describes an expression as a string, and
(d) , which describes an expression as a tree. A(n) (e) is a program that converts
a string following (c) to a tree following (d) .
a) (3pts) Write every bound occurrence and its binding occurrence. (To say that the first a in line
2 is a bound occurrence whose binding occurrence is a in line 7, write 1->10 in your answer.)
b) (2pts) Write every shadowed occurrence and its shadowing occurrence. (To say that the first a
in line 2 is shadowed by a in line 7, write 1->10 in your answer.)
3) (5pts) Write an FAE expression that has a free identifier but evaluates to a value without incurring
any run-time errors.
4
4) (10pts) Consider the following functions:
• bindings: returns the set of every identifier that has at least one binding occurrence in a given expression
• frees: returns the set of every identifier that has at least one free occurrence in a given expression
For example, bindings(e) and frees(e) evaluate to Set("x") and Set("y") respectively, where e
denotes { val x = 1; y }. Consider the following implementation of bindings and frees for RFAE:
def bindings(e: Expr): Set[String] = e match {
case Num(n) => Set()
case Id(x) => Set()
case Val(x, e, b) => (bindings(e) ++ bindings(b)) + x
case App(f, a) => bindings(f) ++ bindings(a)
case Fun(x, b) => (a)
case Rec(f, x, b, e) => (b) }
def frees(e: Expr): Set[String] = e match {
case Num(n) => Set()
case Id(x) => Set(x)
case Val(x, e, b) => frees(e) ++ (frees(b) - x)
case App(f, a) => frees(f) ++ frees(a)
case Fun(x, b) => (c)
case Rec(f, x, b, e) => (d) }
Fill the blanks to complete the implementation. You may refer to the following to deal with sets:
• Where s is a set, s + v denotes s ∪ {v}, and s - v denotes s \ {v}.
• Where s1 and s2 are sets, s1 ++ s2 denotes s1 ∪ s2, and s1 -- s2 denotes s1 \ s2.
5) (10pts) Consider PFAE, which extends FAE with pairs:
case class Pair(f: Expr, s: Expr) extends Expr
case class Fst(e: Expr) extends Expr
case class Snd(e: Expr) extends Expr
Pair creates a new pair; Fst gives the first value of a pair; Snd gives the second value of a pair.
This question asks you to implement the desugar function, which takes a PFAE expression and returns
an expression that lacks Pair, Fst, and Snd but has the same behavior as the given expression. Precisely
speaking, desugar satisfies the following:
• If e evaluates to an integer, desugar(e) evaluates to the same integer.
• If e evaluates to a function, desugar(e) evaluates to a function.
• If e evaluates to a pair, desugar(e) evaluates to a function.
• If e does not terminate, desugar(e) does not terminate.
• If e incurs a run-time error, desugar(e) can have any behavior.
def desugar(e: Expr): Expr = e match {
case Num(n) => Num(n)
case Id(x) => Id(x)
case Fun(x, b) => Fun(x, desugar(b))
case App(f, a) => App(desugar(f), desugar(a))
case Fst(e) =>
// Apply ‘e‘ to a function that takes two arguments and returns the former
App(desugar(e), Fun("x", Fun("y", Id("x"))))
case Snd(e) =>
// Apply ‘e‘ to a function that takes two arguments and returns the latter
App(desugar(e), Fun("x", Fun("y", Id("y"))))
case Pair(f, s) => A }
Fill the blank to complete the implementation.
5
6) (5pts) Write the result of each of the following FAE expressions:
a) {
val fac = { n =>
if0 (n) { 1 } else { (n * fac((n - 1))) }
};
fac(5)
}
b) {
val facX = { facY => { n =>
if0 (n) { 1 } else { (n * facY(facY)((n - 1))) }
}};
facX(facX)(5)
}
c) {
val facX = { facY => { n =>
if0 (n) { 1 } else { (n * facY(facY)((n - 1))) }
}};
{
val fac = { n => facX(facX)(n) };
fac(5)
}
}
d) {
val facX = { facY => { n =>
if0 (n) { 1 } else { (n * facY(facY)((n - 1))) }
}};
{
val fac = facX(facX);
fac(5)
}
}
e) {
val facX = { facY => { n => {
val fac = facY(facY);
if0 (n) { 1 } else { (n * fac((n - 1))) }
}}};
{
val fac = { n => facX(facX)(n) };
fac(5)
}
}
You will get max(0, (number of correct answers) − 2 × (number of wrong answers)) points. If you do
not write anything, it will be considered neither correct nor wrong.
6
7) (10pts) If a language supports mutation, we can implement mkRec more easily. Consider the following
BFAE expression:
{
val mkRec = { b => {
val box = Box({ x => y });
{
val f = b( (a) );
{
box.set( (b) );
(c)
}
}
}};
{
val fac = mkRec({ fac => {
n => if0 (n) { 1 } else { (n * fac((n - 1))) }
}});
fac(5)
}
}
The key idea is to put a dummy function in a box, create a recursive function with the box, and
replace the dummy function with the correct recursive function. Fill the blanks to complete the
implementation. fac must be a function computing the factorial of a given integer, and the result of
the whole expression must be 120.
8) (5pts) Consider the following interp for MFAE that prints given arguments each time it is called:
def malloc(sto: Sto): Addr = (sto.keySet + 0).max + 1
7
9) (10pts) This question implements an interpreter for PMFAE, which extends MFAE with pointers. Pointers
are addresses as values. A pointer to a variable x denotes the address of x. For this purpose, we add
new sorts of an expression and a value as follows:
e ::= · · · | &x | ∗ e | ∗ e := e
v ::= · · · | a
We implement them as follows:
case class Ref(x: String) extends Expr
case class Deref(p: Expr) extends Expr
case class Assign(p: Expr, e: Expr) extends Expr
case class PtrV(a: Addr) extends Value
Ref(x) denotes &x; Deref(e) denotes ∗e; Assign(e1 , e2 ) denotes ∗e1 := e2 ; PtrV(a) denotes a.
The semantics of the new expressions is as follows:
x ∈ Domain(σ) σ, M1 ` e ⇒ a, M2 a ∈ Domain(M2 )
σ, M ` &x ⇒ σ(x), M σ, M1 ` ∗e ⇒ M2 (a), M2
σ, M1 ` e1 ⇒ a, M2 σ, M2 ` e2 ⇒ v, M3
σ, M1 ` ∗e1 := e2 ⇒ v, M3 [a 7→ v]
8
10) (5pts) The following figure describes mark-and-sweep garbage collection. Initially, the heap is full
with four records. The garbage collector attaches the W (white) tag to each record and starts marking.
After marking all the live records, the garbage collector frees unreachable records. As a result, only
three records remain.
Write all the missing steps to complete the figure. Do not draw the heap; write only the tags of the
records in each step. There are three kinds of a tag: W, G (gray), or B (black). When writing the tags
in a single step, follow the “Z” order (top-left, top-right, bottom-left, bottom-right) to traverse the
records. For example, to claim that the following figure is correct, write WGBW, BGWB as your answer.
9
11) (15pts) Suppose that a garbage-collected interepreter uses the following three kinds of a record:
• Tag 1: a record containing one integer
• Tag 2: a record containing one pointer
• Tag 99: forwarding pointer (to to-space)
The interpreter has one register, which always contains a pointer, and a memory pool of size 24. Its
garbage collector is a two-space copying collector, so each space is of size 12. Records are allocated
consecutively in from-space, starting from 0. Consider the following garbage collector implementation:
// Runs two-space copying GC while showing the memory after each step
def gc() = {
show()
reg = copy(reg); show()
while (scan < free) { doScan(); show() }}
10
12) (5pts) Consider the following interp for LFAE that prints given arguments each time it is called:
def strict(v: Value): Value = v match {
case ExprV(e, env) => strict(interp(e, env))
case _ => v }
def interp(e: Expr, env: Env): Value = {
println(s"$e, $env")
e match {
case Num(n) => NumV(n)
case Add(l, r) => (strict(interp(l, env)), strict(interp(r, env))) match {
case (NumV(x), NumV(y)) => NumV(x + y)
case _ => error() }
case Id(x) => env(x)
case Fun(x, b) => CloV(x, b, env)
case App(f, a) => strict(interp(f, env)) match {
case CloV(x, b, fenv) => interp(b, fenv + (x -> ExprV(a, env)))
case _ => error() }}}
Let e be an expression { x => (x + x) }((1 + 2)), which is App(Fun("x", Add(Id("x"), Id("x"))),
Add(Num(1), Num(2))). What does interp(e, Map()) print? You can use any notation you prefer
in your answer. If you think your notation is confusing, describe what your notation means in the
answer.
13) (10pts) This question asks you to extend FAE with promises, which originate from the Racket pro-
gramming language. A promise is a value that encapsulates an expression to be evaluated on demand
via force. Due to the introduction of promises, values are now defined as follows:
v ::= n | hλx.e, σi | delay(e, σ) | lazy(e, σ)
While n and hλx.e, σi are usual number and closure values, delay(e, σ) and lazy(e, σ) are two different
kinds of a promise value. They are similar in that both encapsulate expressions, but their behaviors
are slightly different when being forced.
When a value is forced, a value comes out as the result. The semantics of force is as follows:
• n
When forced, it produces n as the result.
• hλx.e, σi
When forced, it produces hλx.e, σi as the result.
• delay(e, σ)
When forced, it evaluates e under σ and produces e’s result as the result.
• lazy(e, σ)
When forced, it evaluates e under σ. Then, e’s result is also forced to obtain a value v. v is the
result of forcing lazy(e, σ).
There are three kinds of an expression treating a promise: e ::= · · · | delay e | lazy e | force e.
• delay e
Creates a delay promise with e and the current environment.
• lazy e
Creates a lazy promise with e and the current environment.
• force e
Evaluates e and forces e’s result to obtain the result.
a) (5pts) Define the semantics of force of the form v ⇓ v . v1 ⇓ v2 means forcing v1 results in v2 .
b) (5pts) Define the operational semantics of the form σ ` e ⇒ v for delay e, lazy e, and force e.
—End of Exam—
11