alpha -> (alpha -> beta) -> betaLet us try to construct an expression with this type. Clearly, it must be a (curried) function with two arguments, i.e. must have the following structure.

fn ... => fn ... => ...Let us now fill in the dots. Let us call

Let us use an analogy: think of` alpha `as milk, and
of` beta `as` yogurt`. Then` f `represents a machine
that transforms milk into yogurt.
Now, we have some milk (`x`), we have the machine, and we want
to obtain yougurt.
What shall we do? The answer is, of course,

turn on the machine and put the milk in iti.e. use

fn x => fn f => f x

(alpha * beta -> gamma) -> alpha -> beta -> gammaAn expression with this type must have the following structure:

fn f => fn x => fn y => ...where

Look at` f `as a machine that, when
provided with flour (`alpha`) and` yeast `(`beta`)
together, it makes bread (`gamma`).
We have some flour (`x`), we have some ` yeast `(`y`),
how to obtain bread?
The answer is, of course,

useHence the solution is:fand give it in inputxandytogether.

fn f => fn x => fn y => f(x,y)

fn f => f fLet us see why it does not have a type. If it did, it should be something like

(alpha -> beta) -> betawhere

For a more intuitive explanation: Think again
of` f `as a machine
that transforms milk into yogurt. We can clone (make a copy) of
this machine and try to feed the first machine
with the second, but it won't work.

fn f => fn x => (f x, f(x,x))Intuitively, the type of

Note that if we write in ML the above expressions, we get a type error.

(alpha -> beta) -> beta (alpha -> beta) -> alpha beta -> (alpha -> beta) -> alpha alpha -> alpha * betaIf we try to construct an expression for any of these types, we won't succeed. Consider for instance the first type. We have a machine that tranforms milk into yogurt, but we don't have any milk. How can we obtain yogurt? The answer is "We can't". (Feeding the machine with (a copy of) itself does not work, as seen before. Selling the machine and using the money to buy yogurt is not allowed :-)

`->`corresponds to implication`*`corresponds to conjunction- Type variables correspond to propositions

For instance, the type## Theorem

A type is inhabited (i.e. not empty) only if it corresponds to a logically valid formula (i.e. a tautology).

Hence we can give the following criterion for the
emptyness of a type` t`:

ifThe reverse does not hold: there are types which are tautologies, but still are empty. One example of such a type istis not a tautology, then we know thattis empty

((alpha -> alpha) -> alpha) -> alphaIn the sub-theory of Classical Logic called

A type is inhabited (i.e. not empty) if and only if it corresponds to a formula intuitionistically valid.Sub-theory here means that less formulae are valid.

fn x => xThe type of this expression is

alpha -> alphaLet us call

beta * gamma -> beta * gamma (beta -> gamma) -> beta ->gamma beta list -> beta list ...The difference between

This result is nice because it allows us to consider only the most general type## Theorem

If an expression has a type, then it has a most general type.

In general, when we construct the type of an expression,
we derive first the structure` s `of the type, and then, by
analysing the expression, a set of equations` E `
between type variables.
The final type is then obtained by instantiating all the
type variables in` s `so to reflect the constraints
imposed by the equations.

More precisely, in order to make sure that we do not miss any constraint,
and that we do not impose constraints that aren't necessary,
we should instantiate` s `by using the "most general solution"
of the set of equations` E`.
A solution for` E `is an association between type variables and types
which validates all the equations.

A solution for` E `can be
expressed as a system of equations ` E'` in *solved form*, i.e.
such that the left hand sides of each equation is a distinct type variable,
and the right hand sides are type expressions containing none
of the variables of the lhs.
We say that a
solution` E' `for` E `is * most general * if` E' `is
equivalent to` E`, i.e. ` E `and` E' `have
exactly the same solutions.

The most general solution for` E `can be obtained by repeatedly
performing the following operations on ` E`:

- Simplify the equations. For instance, an equation
alpha -> beta = gamma -> delta * epsilon

should be replaced by the two equationsalpha = gamma beta = delta * epsilon

- Substitute the variables with their solution.
For instance, if we have the equation
alpha = gamma -> delta * epsilon

then we should replace`alpha`by`gamma -> delta * epsilon`in every other equations

fn f => fn x => (f (f x))Its type will have the following structure

alpha -> beta -> gammaWith

alpha = beta -> epsilon, where (f x): epsilon, and alpha = epsilon -> gammaThe most general solution of this set of equations can be obtained by first replacing

alpha = beta -> epsilon beta -> epsilon = epsilon -> gammathen simplify the second equation:

alpha = beta -> epsilon beta = epsilon epsilon = gammathen, replace

alpha = beta -> gamma beta = gamma epsilon = gammaFinally, replace

alpha = gamma -> gamma beta = gamma epsilon = gammaThis final system is in solved form, hence it is the most general solution of

(gamma -> gamma) -> gamma -> gammaNote that, when we solve a system of equations, there are in general several ways to proceed, depending on the choice of the particular equation considered at every step. Different choices bring to different (but equivalent) formulation of the result, and to different names in the final type. This does not matter, since the names of type variables are not important.

fun f x = ewithout recursion. In order to derive the type of

val f = fn x => enow we can derive the type of

With a little bit of fantasy, we can apply the reasoning directly,
without the need of transforming the` fun ` declaration
into a` val ` declaration.

fun f v [] = v | f v (x::l) = (x,x) :: []

alpha -> beta -> gammaWe have the following constraints:

gamma = alpha, from the result in the fst line beta = delta list, from the pattern of the snd arg in the fst line gamma = (phi * phi) list, from the result in the snd line (where x: phi) delta = phi, from the pattern of the snd arg in the snd lineA solved form of this system is

beta = phi list, gamma = (phi * phi) list, alpha = (phi * phi) list, delta = phi,Which, applied to

(phi * phi) list -> phi list -> (phi * phi) list

fun f v [] = [v] | f v (x::l) = f x l

alpha -> beta -> gammaWe have the following constraints:

gamma = alpha list, from the result in the fst line beta = delta list, from the pattern of the snd arg in the fst line delta = alpha, from the recursive call in the snd lineA solution is

gamma = alpha list beta = alpha list delta = alphawhich, applied to

alpha -> alpha list -> alpha list

The type system consists in a set of rules which define, inductively, the relation

r |- e : t (e has type t under the assumptions r)where:

`e`is a Mini-ML expression`t`is a Mini-ML type`r`is a set of associations between type-variables and types

Note the analogy of the type statement` r |- e : t `and the
evaluation statement` env |- e eval t` in past
lecture notes. Due to this analogy,
the type system is sometimes called "static semantics", and` r `is
called "static environment". The roles of` r `and` env `are
very similar, and their behaviors define the same scoping rules.

Exp ::= Ide (identifiers) | Exp Exp (functional application) | fn Ide => Exp (functional abstraction 1) | fn (Ide,Ide) => Exp (functional abstraction 2) | (Exp,Exp) (pairing) | Exp :: Exp (cons on lists) | hd Exp (head of a list) | tl Exp (tail of a list) | nil (empty list) Type ::= TVar (type variables, i.e. parametres for types) | Type * Type (Cartesian product, the type of pairs) | Type -> Type (functional type, the type of functions) | Type list (the type of lists)

**Core of Mini ML (corresponding to the lambda calculus)**(ide) ------------------ (where x is an identifier) r |- x : r(x) r |- e1 : alpha -> beta r |- e2 : alpha (app) ---------------------------------------------- r |- (e1 e2) : beta r[x:alpha] |- e : beta (abs1) ---------------------------------- r |- (fn x => e) : alpha -> beta

**Pairs**r[x:alpha][y:beta] |- e : gamma (abs2) ----------------------------------------------- r |- fn (x,y) => e : (alpha * beta) -> gamma r |- e1 : alpha r |- e2 : beta (pair) --------------------------------------- r |- (e1,e2) : alpha * beta

**Lists**r |- e1 : alpha r |- e2 : alpha list (cons) ------------------------------------------- r |- (e1 :: e2) : alpha list r |- e : alpha list (head) ----------------------- r |- (hd e) : alpha r |- e : alpha list (tail) ---------------------------- r |- (tl e) : alpha list (nil) ---------------------------- r |- nil : alpha list

We say that` e `has type` t `if we can derive the
statement` emptyset |- e : t`.

In order to derive the most general type of an expression,
we need to construct a proof for the statement` emptyset |- e : alpha`,
and then instantiate` alpha `with the solution of the constraint
that we find in this proof (i.e. the constraints imposed by
the particular form of the types in the conclusion of the rules)
from the` var `rule. A similar process allows us to
derive an expression for a given type` t`.