Commit c24814e9 authored by REMY Didier's avatar REMY Didier


parents 12d27e9b 054e4511
# Functional programming and type systems (2017-2018)
[The official MPRI
This page supplements
[the official page of MPRI 2-4](
## Location
## Location and duration
The lessons take place at University of Paris 7 - Denis Diderot,
Batiment Sophie Germain in room 2035 on Fridays at 12:45 and ends at 15:30.
There will be a break of 15' in the middle of the course.
The lectures take place at University Paris 7 - Denis Diderot,
Bâtiment Sophie Germain, in room 2035.
They are scheduled on Fridays from 12:45 to 15:30.
There is a 15-minute break in the middle of each lecture.
## Teachers
......@@ -20,6 +22,7 @@ There will be a break of 15' in the middle of the course.
This course presents the principles and formalisms that underlie many of
today's typed functional programming languages.
(Here are some [introductory slides](slides/fpottier-00.pdf).)
The course is made up of four parts and can be split after the first two
......@@ -43,8 +46,8 @@ systems. We study parametric polymorphism (as in System F and ML), data
types and type abstraction. We show syntactic type soundness (via progress
and subject reduction) by reasoning by induction on typing derivations. We
also show how to obtain semantic properties via logical relations by
reasoning by induction on the structure of types. We also introduce
subtyping and row polymorphism and illustrate typing problems induced by
reasoning by induction on the structure of types. Finally, we introduce
subtyping, row polymorphism, and illustrate the problems induced by
side effects (references) and the need for the value restriction.
The third part of the course describes more advanced features of type
......@@ -61,25 +64,50 @@ We also show the limits of dependently-typed functional programming.
### Functional Programming: Under the Hood
* (22/09/2017) From a small-step operational semantics...
* (29/09/2017) ... to an efficient interpreter. (2 weeks.)
* (06/10/2017) Compiling away first-class functions: closure conversion, defunctionalization.
* (13/10/2017) Compiling away the call stack: the CPS transformation.
* (20/10/2017) Equational reasoning and program optimizations.
* (22/09/2017)
Introduction ([slides 00](slides/fpottier-00.pdf)).
Syntax and operational semantics, on paper and on a machine
([slides 01a](slides/fpottier-01a.pdf))
([slides 01b](slides/fpottier-01b.pdf)).
* Newton-Raphson in OCaml ([solution](ocaml/
* 1 is not even in Coq ([Even.v](coq/Even.v)).
* (29/09/2017)
From a small-step semantics down to an efficient verified interpreter,
in several stages
([Coq demo](coq/DemoSyntaxReduction.v))
([slides 02](slides/fpottier-02.pdf))
([OCaml code](ocaml/
([Coq repo](coq/)).
* (06/10/2017) Compiling away first-class functions:
closure conversion, defunctionalization
([slides 03](slides/fpottier-03.pdf))
([Coq repo](coq/)).
* (13/10/2017) Making the stack explicit: the CPS transformation
([slides 04](slides/fpottier-04.pdf))
([Coq repo](coq/)).
* Transforming a call-by-value interpreter
([exercise](ocaml/, [solution](ocaml/
* Transforming a call-by-name interpreter
* Transforming a graph traversal
* (20/10/2017) Equational reasoning and program optimizations
([slides 05](slides/fpottier-05.pdf))
([Coq mini-demo](coq/DemoEqReasoning.v)).
### Metatheory of Typed Programming Languages
* (15/09/2017)
[Metatheory of System F](
(See also [intro](,
and chap [1](
and [2](
(See also [intro](,
and chap [1](
and [2](
of [course notes](
* (27/10/2017)
[ADTs, existential types, GADTs]
* (03/11/2017) Logical relations.
* (10/11/2017) Subtyping. Rows.
* (10/11/2017) Subtyping. Rows.
* (17/11/2017) References, Value restriction, Side effects.
### Advanced Aspects of Type Systems
......@@ -108,24 +136,24 @@ who split the course.
Although the course has changed, you may still have a look at previous exams
available with solutions:
- mid-term exam 2016-2017:
- mid-term exam 2016-2017:
[Record concatenation](
- mid-term exam 2015-2016:
- mid-term exam 2015-2016:
[Type containment](
- final exam 2014-2015: [Copatterns](
- mid-term exam 2014-2015:
- mid-term exam 2014-2015:
[Information flow](
- final exam 2013-2014:
- final exam 2013-2014:
[Operation on records](
- mid-term exam 2013-2014:
- mid-term exam 2013-2014:
[Typechecking Effects](
- final exam 2012-2013:
- final exam 2012-2013:
[Refinement types](
- mid-term exam 2012-2013:
- mid-term exam 2012-2013:
[Variations on ML](
- final exam 2011-2012:
- final exam 2011-2012:
[Intersection types](
- mid-term exam 2011-2012:
- mid-term exam 2011-2012:
- final exam 2010-2011:
[Compiling a language with subtyping](
......@@ -135,19 +163,58 @@ available with solutions:
## Recommended software
OCaml 4.0x and Coq **8.5**.
Please install [opam]( first.
Once you have installed [opam](, use the following commands:
Then, install OCaml 4.0x and Coq **8.5** via the following commands:
opam init --comp=4.05 # for instance
opam repo add coq-released
opam update
opam install -j4 -v coq.8.5.3
(Do *not* install Coq 8.6. The version of AutoSubst that I am using is
not compatible with it. If for some reason you need Coq 8.6, or have
already installed Coq 8.6, note that `opam switch` can be used to let
multiple versions of Coq coexist.)
Please also install François Pottier's
of the
[AutoSubst]( library:
git clone
make -C autosubst install
In order to use Coq inside `emacs`,
is highly recommended.
Here is a suggested installation script:
rm -rf /tmp/PG
cd /tmp
git clone
cd PG
if [ ! -x $EMACS ]; then
make EMACS=$EMACS compile
sudo rm -rf $TARGET
sudo mv /tmp/PG $TARGET
Enable ProofGeneral by adding the following line to your `.emacs` file:
(load-file "/usr/local/share/emacs/site-lisp/ProofGeneral/generic/proof-site.el")
If desired, ProofGeneral can be further
## Bibliography
[Types and Programming Languages](,
[Types and Programming Languages](,
Benjamin C. Pierce, MIT Press, 2002.
[Advanced Topics in Types and Programming Languages](,
Require Import Omega.
Require Import Autosubst.Autosubst.
Require Import MyTactics. (* TEMPORARY *)
(* -------------------------------------------------------------------------- *)
(* A more recognizable notation for lifting. *)
Notation lift i t := (t.[ren(+i)]).
(* -------------------------------------------------------------------------- *)
Section Extras.
Context A `{Ids A, Rename A, Subst A, SubstLemmas A}.
Lemma up_ren:
forall xi,
ren (upren xi) = up (ren xi).
intros. autosubst.
Lemma upn_ren:
forall i xi,
ren (iterate upren i xi) = upn i (ren xi).
induction i; intros.
{ reflexivity. }
{ rewrite <- fold_up_upn. rewrite <- IHi. asimpl. reflexivity. }
Lemma plus_upn: (* close to [up_liftn] *)
forall i sigma,
(+i) >>> upn i sigma = sigma >> ren (+i).
induction i; intros.
{ rewrite iterate_0. autosubst. }
{ rewrite iterate_S. asimpl. rewrite IHi. autosubst. }
Lemma up_sigma_up_ren:
forall t i sigma,
t.[up sigma].[up (ren (+i))] = t.[up (ren (+i))].[upn (1 + i) sigma].
intros. asimpl. rewrite plus_upn. asimpl. reflexivity.
Lemma upn_k_sigma_x:
forall k sigma x,
x < k ->
upn k sigma x = ids x.
induction k; intros; asimpl.
{ omega. }
{ destruct x; asimpl.
{ eauto. }
{ rewrite IHk by omega. autosubst. }
Lemma push_substitution_last:
forall t v i,
t.[v .: ren (+i)] = t.[up (ren (+i))].[v/].
intros. autosubst.
Lemma push_substitution_last_up_hoist:
forall t v i j,
t.[up (v .: ren (+i))].[up (ren (+j))] =
t.[up (up (ren (+j + i)))].[up (lift j v .: ids)].
intros. autosubst.
Lemma lift_lift:
forall i j t,
lift i (lift j t) = lift (i + j) t.
intros. autosubst.
Lemma lift_upn:
forall t i sigma,
(lift i t).[upn i sigma] = lift i t.[sigma].
intros. asimpl.
erewrite plus_upn.
Lemma lift_up:
forall t sigma,
(lift 1 t).[up sigma] = lift 1 t.[sigma].
intros. change up with (upn 1). eapply lift_upn.
Lemma up_sigma_f:
forall (sigma : var -> A) (f : A -> A),
f (ids 0) = ids 0 ->
(forall i t, lift i (f t) = f (lift i t)) ->
up (sigma >>> f) = up sigma >>> f.
intros. f_ext. intros [|x]; asimpl; eauto.
Lemma upn_sigma_f:
forall (sigma : var -> A) (f : A -> A),
f (ids 0) = ids 0 ->
(forall i t, lift i (f t) = f (lift i t)) ->
forall i,
upn i (sigma >>> f) = upn i sigma >>> f.
induction i; intros.
{ reflexivity. }
{ do 2 rewrite <- fold_up_upn. rewrite IHi. erewrite up_sigma_f by eauto. reflexivity. }
Lemma upn_theta_sigma_ids:
forall theta sigma i,
theta >> sigma = ids ->
upn i theta >> upn i sigma = ids.
intros theta sigma i Hid.
rewrite up_comp_n.
rewrite Hid.
rewrite up_id_n.
Lemma up_theta_sigma_ids:
forall theta sigma,
theta >> sigma = ids ->
up theta >> up sigma = ids.
change up with (upn 1). eauto using upn_theta_sigma_ids.
Lemma scons_scomp:
forall (T : A) Gamma theta,
T.[theta] .: (Gamma >> theta) = (T .: Gamma) >> theta.
intros. autosubst.
(* BUG: the two sides of this equation are distinct, yet they are
printed identically. *)
forall v f,
v .: (ids >>> f) = (v .: ids) >>> f.
Fail reflexivity.
End Extras.
(* This incantation means that [eauto with autosubst] can use the tactic
[autosubst] to prove an equality. *)
Hint Extern 1 (_ = _) => autosubst : autosubst.
(* This incantation means that [eauto with autosubst] can use the lemmas
whose names are listed here. This is useful when an equality involves
metavariables, so the tactic [autosubst] fails. *)
Hint Resolve scons_scomp : autosubst.
(* -------------------------------------------------------------------------- *)
Require Import Omega.
Require Import Autosubst.Autosubst.
Require Import AutosubstExtra. (* just for [upn_ren] *)
Require Import MyTactics. (* TEMPORARY *)
(* This file defines the construction [eos x t], which can be understood as
an end-of-scope mark for [x] in the term [t]. *)
(* It also defines the single-variable substitution t.[u // x], which is the
substitution of [u] for [x] in [t]. *)
(* -------------------------------------------------------------------------- *)
Section EOS.
Context {A} `{Ids A, Rename A, Subst A, SubstLemmas A}.
(* The substitution [Var 0 .: Var 1 .: ... .: Var (x-1) .: Var (x+1) .: ...]
does not have [Var x] in its codomain. Thus, applying this substitution
to a term [t] can be understood as an end-of-scope construct: it means
``end the scope of [x] in [t]''. We write [eos x t] for this construct.
It is also known as [adbmal]: see Hendriks and van Oostrom, *)
(* There are at least two ways of defining the above substitution. One way
is to define it in terms of AutoSubst combinators: *)
Definition eos_var x : var -> var :=
(iterate upren x (+1)).
Definition eos x t :=
t.[ren (eos_var x)].
Lemma eos_eq:
forall x t,
t.[upn x (ren (+1))] = eos x t.
intros. unfold eos, eos_var. erewrite upn_ren by tc. reflexivity.
(* Another way is to define directly as a function of type [var -> var]. *)
Definition lift_var x : var -> var :=
fun y => if le_gt_dec x y then 1 + y else y.
(* The two definitions coincide: *)
Lemma upren_lift_var:
forall x,
upren (lift_var x) = lift_var (S x).
intros. f_ext; intros [|y].
{ reflexivity. }
{ simpl. unfold lift_var, var. dblib_by_cases; omega. }
Lemma eos_var_eq_lift_var:
eos_var = lift_var.
(* An uninteresting proof. *)
f_ext; intros x.
unfold eos_var.
induction x.
{ reflexivity. }
{ rewrite iterate_S.
rewrite IHx.
rewrite upren_lift_var.
reflexivity. }
(* -------------------------------------------------------------------------- *)
(* [eos] enjoys certain commutation laws. *)
(* Ending the scope of variable [k], then the scope of variable [s], is the
same as first ending the scope of variable [1 + s], then ending the scope
of variable [k]. This holds provided [k <= s] is true, i.e., [k] is the
most recently-introduced variable.*)
Lemma lift_var_lift_var:
forall k s,
k <= s ->
lift_var s >>> lift_var k = lift_var k >>> lift_var (S s).
(* By case analysis. *)
intros. f_ext; intros x. asimpl.
unfold lift_var, var. dblib_by_cases; omega.
Lemma eos_eos:
forall k s t,
k <= s ->
eos k (eos s t) = eos (1 + s) (eos k t).
intros. unfold eos. asimpl.
rewrite eos_var_eq_lift_var.
rewrite lift_var_lift_var by eauto.
(* What about the case where [k] is the least recently-introduced variable?
It is obtained by symmetry, of course. *)
Lemma eos_eos_reversed:
forall k s t,
k >= s + 1 ->
eos k (eos s t) = eos s (eos (k - 1) t).
replace k with (1 + (k - 1)) by omega.
rewrite <- eos_eos by omega.
replace (1 + (k - 1) - 1) with (k - 1) by omega.
(* -------------------------------------------------------------------------- *)
(* Single-variable substitutions. *)
(* [subst_var u x] is the substitution of [u] for [x]. *)
(* We give a direct definition of it as a function of type [var -> term],
defined by cases. I don't know if it could also be nicely defined in
terms of the basic combinators of de Bruijn algebra. Note that the
candidate definition [upn x (t .: ids)] is WRONG when [x > 0]. *)
Definition subst_var (u : A) (x y : var) : A :=
match lt_eq_lt_dec y x with
| inleft (left _) => ids y
| inleft (right _) => u
| inright _ => ids (y - 1)
(* A nice notation: [t.[u // x]] is the substitution of [u] for [x] in [t]. *)
Notation "t .[ u // x ]" := (subst (subst_var u x) t)
(at level 2, u at level 200, left associativity,
format "t .[ u // x ]") : subst_scope.
(* The following laws serve as sanity checks: we got the definition right. *)
Lemma subst_var_miss_1:
forall x y u,
y < x ->
(ids y).[u // x] = ids y.
intros. asimpl. unfold subst_var. dblib_by_cases. reflexivity.
Lemma subst_var_match:
forall x u,
(ids x).[ u // x ] = u.
intros. asimpl. unfold subst_var. dblib_by_cases. reflexivity.
Lemma subst_var_miss_2:
forall x y u,
x < y ->
(ids y).[u // x] = ids (y - 1).
intros. asimpl. unfold subst_var. dblib_by_cases. reflexivity.
(* In the special case where [x] is 0, the substitution [t // 0] can also
be written [t/], which is an AutoSubst notation for [t .: ids]. *)
Lemma subst_var_0:
forall t u,
t.[u // 0] = t.[u/].
intros. f_equal. clear t.
f_ext. intros [|x].
{ reflexivity. }
{ unfold subst_var. simpl. f_equal. omega. }
(* -------------------------------------------------------------------------- *)
(* A cancellation law: substituting for a variable [x] that does not occur in
[t] yields just [t]. In other words, a substitution for [x] vanishes when
it reaches [eos x _]. *)
(* In informal syntax, this lemma would be written:
t[u/x] = t
under the hypothesis that x does not occur free in t.
In de Bruijn style, the statement is just as short, and does not have a
side condition. Instead, it requires an explicit [eos x _] to appear at the
root of the term to which the substitution is applied; this may require
rewriting before this lemma can be applied. *)
Lemma subst_eos:
forall x t u,
(eos x t).[u // x] = t.
(* Again, let's simplify this first. *)
unfold eos. asimpl.
(* Aha! We can forget about [t], and focus on proving that two
substitutions are equal. To do so, it is sufficient that
their actions on a variable [y] are the same. *)
rewrite <- subst_id.
f_equal. clear t.
f_ext. intro y.
(* The proof is easy if we replace [eos_var] with [lift_var]. *)
rewrite eos_var_eq_lift_var. simpl.
unfold subst_var, lift_var. dblib_by_cases; f_equal; omega.
(* The above property allows us to prove that [eos x _] is injective.
Indeed, it has an inverse, namely [u // x], where [u] is arbitrary. *)
Lemma eos_injective:
forall x t1 t2,
eos x t1 = eos x t2 ->
t1 = t2.
pose (u := t1). (* dummy *)
erewrite <- (subst_eos x t1 u).
erewrite <- (subst_eos x t2 u).