21 1 Midterm
21 1 Midterm
Instructions
• We use parentheses to represent expressions clearly. For example, we may write λx.(x + x) instead of
λx.x + x to make sure that we do not mean (λx.x) + x. You can use parentheses in your answers.
• We often omit the terms “evaluation” and “evaluating” for brevity. For example, “the result of e is v”
means that the result of evaluating e is v, and “e terminates” means that evaluation of e terminates.
• When you write expressions in your answers, you can freely choose between concrete syntax and
abstract syntax notations to write the expressions. For example, both { x => x } and λx.x are
possible.
• There is an appendix at page 9. The appendix shows the definitions of FAE, RFAE, and MFAE. If you
need the definitions to solve a question, refer to the appendix.
1
1) (10pts) The following sentences explain basic concepts in programming languages.
Fill from (A) to (J) with the following terms. (1pt each)
2) (10pts) This question asks you why we need closures. Consider the following semantics without
closures:
σ ` e1 ⇒ λx.e σ ` e2 ⇒ v 2 [x 7→ v2 ] ` e ⇒ v
v ::= n | λx.e σ ` λx.e ⇒ λx.e
σ ` e1 e2 ⇒ v
a) (5pts) Explain why the above semantics, which lacks closures, is problematic in one sentence.
Your explanation must include the terms “free identifier” and “environment”.
b) (5pts) Write an FAE expression such that
• the evaluation in the original semantics with closures results in an integer, but
• the evaluation in the above semantics without closures incurs a run-time error.
2
3) (5pts) Consider the following RFAE expression:
{
val f = { x =>
1 2
if0 (x) {
3
0
} else {
f((x - 1))
4 5
}
};
{
def f(x) =
6 7
if0 (x) {
8
0
} else {
f((x - 1))
9 10
};
f(y)
11 12
}
}
(The numbers below the identifiers are not parts of the expression. They are for convenience of writing
answers. Use the numbers to denote identifiers in your answers.)
a) (3pts) Write every bound occurrence and its binding occurrence. (To say that x in line 2 is a
bound occurrence whose binding occurrence is f in line 2, write 2->1 in your answer.)
b) (2pts) Write every free identifier. (To say that f in line 2 is a free identifier, write 1 in your
answer.)
{
def f(n) = if0 (n) { 0 } else { (n + f((n - 1))) };
{
val f = { x => (10 + f(x)) };
f(4)
}
}
What is the result of this expression under each of the following scoping strategies?
a) (2pts) Static scoping
b) (3pts) Dynamic scoping
3
5) (10pts) The following code is an excerpt from the implementation of an interpreter for MFAE:
def interp(expr: Expr, env: Env, sto: Sto): (Value, Sto) = expr match {
...
case App(f, a) => a match {
case Id(name) =>
val (fv, fs) = interp(f, env, sto)
fv match {
case CloV(x, b, fenv) =>
val addr = lookup(name, env)
interp(b, fenv + (x -> addr), fs)
case _ => error("not a closure")
}
case _ =>
val (fv, fs) = interp(f, env, sto)
val (av, as) = interp(a, env, fs)
fv match {
case CloV(x, b, fenv) =>
val addr = malloc(as)
interp(b, fenv + (x -> addr), as + (addr -> av))
case _ => error("not a closure")
}
}
}
For the following subquestions, you can use any notation you prefer in your answers. For example, you
may write [a1 -> 3, a2 -> <{x => x}, []>] to denote the store that maps an address a1 to the
integer 3 and an address a2 to the closure hλx.x, ∅i. If you think your notation is confusing, describe
what your notation means in the answer.
Consider the following MFAE expression:
{
val n = 42;
{
val f = { g => g(n) };
f({ x => (x + 8) })
}
}
b) (4pts) Write the environment and store just before evaluating (x + 8) in the call-by-value se-
mantics.
c) (4pts) Write the environment and store just before evaluating (x + 8) in the call-by-reference
semantics.
4
6) (5pts) Consider the function f in the following FAE expression:
1 {
2 val f = {
3 val fX = { fY => {
4 val f = fY(fY);
5 { n => if0 (n) { 0 } else { (n + f((n - 1))) } }
6 }};
7 fX(fX)
8 };
9 f(10)
10 } (
0 if n = 0
Also, consider the following mathematical function: f (n) =
n + f (n − 1) if n > 0
Is the FAE function the same as the mathematical function? If not, fix the expression to make them
equal. You do not need to justify your answer when it is “yes”. (You can use line numbers to write
your answer.)
7) (10pts) Mutually recursive functions are functions that call each other. For example, the following
Scala code defines two mutually recursive functions even and odd:
5
8) (15pts) Below is an interpreter for FAE written in Scala. Unlike the interpreters in the lectures, the
following interpreter uses substitutions, instead of environments. (For simplicity, the implementation
omits some expressions.)
sealed trait Expr
sealed trait Value extends Expr
case class Num(value: Int) extends Expr with Value
case class Id(x: String) extends Expr
case class Fun(x: String, body: Expr) extends Expr with Value
case class App(fun: Expr, arg: Expr) extends Expr
6
9) (15pts) This question extends LFAE with val, if0, and pairs. Consider the following interpreter
implementation:
7
a) (3pts) Like in FAE, the semantics of val x = e1 ; e2 must be the same as (λx.e2 ) e1 . Fill (B).
b) (3pts) Like in RFAE, if0 e1 e2 e3 evaluates e2 when e1 evaluates to 0 and evaluates e3 when e1
evaluates to a nonzero integer, a closure, or a pair. Fill (C).
c) (9pts) The semantics of pairs is as follows:
• Pair(e1 , e2 ) corresponds to (e1 , e2 ), which is an expression that creates a new pair.
• Fst(e) corresponds to e._1, which is an expression that gives the first value of a given pair.
• Snd(e) corresponds to e._2, which is an expression that gives the second value of a given pair.
Pairs in this language are lazy, which means that e1 and e2 in (e1 , e2 ) are not evaluated when
the pair is created. Each of them is evaluated only when its value is needed. For example,
• (3, ({ x => x } + 4)) does not incur a run-time error.
• ((3, ({ x => x } + 4))._1 + 5) does not incur a run-time error.
• ((3, ({ x => x } + 4))._2 + 5) incurs a run-time error.
Fill (A), (D), (E), and (F). You can put any number of fields in (A).
10) (15pts) This question extends FAE with exceptions.
Expression e ::= n | e + e | x | λx.e | e e | throw | try e catch e
Value v ::= n | hλx.e, σi
Result r ::= v | exc
Due to the presence of exceptions, an expression can throw an exception instead of evaluating to a
value. σ ` e ⇒ v denotes that e evaluates to v under σ, and σ ` e ⇒ exc denotes that e throws an
exception under σ.
Note that errors are different from exceptions. Errors are unintended failures of evaluation, which
cannot be handled. For example, adding an interger to a closure causes an error and terminates the
execution immediately. However, exceptions are thrown by throw written by programmers and can
be handled by try-catch. Therefore, exceptions are thrown and handled according to the intention of
programmers.
The semantics of the language is as follows:
• throw throws an exception.
• Any expressions except try-catch propagate exceptions, i.e. if a subexpression of e that is not
try-catch throws an exception, then e also throws an exception without evaluating the remaining
subexpressions. This language uses the left-to-right order for evaluating subexpressions.
• During the evaluation of each expression, dynamic type checking of the values of the subexpres-
sions happens only after evaluating all the subexpressions. For example, in e1 + e2 , when e1
evaluates to v1 , it is checked whether v1 is an integer or not after the evaluation of e2 .
• try e1 catch e2 handles an exception. If e1 throws an exception, try e1 catch e2 evaluates e2 and
uses the result of e2 as its result. If e1 does not throw an exception, the result of e1 is used as the
result without evaluating e2 .
Below are some examples:
• ∅ ` throw ⇒ exc
• ∅ ` throw + 1 ⇒ exc
• ∅ ` (λx.x) + throw ⇒ exc
• ∅ ` throw + ((λx.(x x)) (λx.(x x))) ⇒ exc
• ((λx.(x x)) (λx.(x x))) + throw does not terminate.
• ∅ ` try 1 catch 2 ⇒ 1
• ∅ ` try throw catch 2 ⇒ 2
a) (10pts) Write the operational semantics of the form σ ` e ⇒ r .
b) (5pts) Draw the evaluation derivation of try (1 + throw) catch (throw + 2).
8
Appendix
• FAE
– Concrete syntax
expr ::= num | "(" expr "+" expr ")" | "(" expr "-" expr ")" | id
| "{" "val" id "=" expr ";" expr "}" | "{" id "=>" expr "}"
| expr "(" expr ")" | "if0" "(" expr ")" "{" expr "}" "else" "{" expr "}"
– Abstract syntax
e ::= n | e + e | e − e | x | val x = e; e | λx.e | e e | if0 e e e
v ::= n | hλx.e, σi
– Semantics
σ ` e1 ⇒ n1 σ ` e2 ⇒ n2 σ ` e1 ⇒ n1 σ ` e2 ⇒ n2
σ`n⇒n
σ ` e1 + e2 ⇒ n1 + n2 σ ` e1 − e2 ⇒ n1 − n2
x ∈ Domain(σ) σ ` e 1 ⇒ v1 σ[x 7→ v1 ] ` e2 ⇒ v2
σ ` x ⇒ σ(x) σ ` val x = e1 ; e2 ⇒ v2
σ ` e1 ⇒ hλx.e, σ 0 i σ ` e 2 ⇒ v2 σ 0 [x 7→ v2 ] ` e ⇒ v
σ ` λx.e ⇒ hλx.e, σi
σ ` e1 e2 ⇒ v
σ ` e1 ⇒ 0 σ ` e 2 ⇒ v2 σ ` e 1 ⇒ v1 v1 6= 0 σ ` e 3 ⇒ v3
σ ` if0 e1 e2 e3 ⇒ v2 σ ` if0 e1 e2 e3 ⇒ v3
• RFAE (The omitted parts are the same as FAE.)
– Concrete syntax
e ::= ... | "{" "def" id "(" id ")" "=" expr ";" expr "}"
– Abstract syntax
e ::= · · · | def x(x) = e; e
– Semantics
σ 0 = σ[x1 7→ hλx2 .e1 , σ 0 i] σ 0 ` e 2 ⇒ v2
σ ` def x1 (x2 ) = e1 ; e2 ⇒ v2
• MFAE
– Concrete syntax (The omitted parts are the same as FAE.)
e ::= ... | "{" id "=" expr "}"
– Abstract syntax (The omitted parts are the same as FAE.)
e ::= · · · | x := e
– Semantics (Some rules are omitted for brevity.)
x ∈ Domain(σ) σ(x) ∈ Domain(M )
σ, M ` n ⇒ n, M σ, M ` λx.e ⇒ hλx.e, σi, M
σ, M ` x ⇒ M (σ(x)), M
—End of Exam—