Download View raw file - aaa

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Lambda lifting wikipedia , lookup

Lambda calculus wikipedia , lookup

Combinatory logic wikipedia , lookup

Lambda calculus definition wikipedia , lookup

Curry–Howard correspondence wikipedia , lookup

Intuitionistic type theory wikipedia , lookup

C Sharp (programming language) wikipedia , lookup

Standard ML wikipedia , lookup

Transcript
Implementing a Dependently
Typed λ-Calculus
Ali Assaf
Abbie Desrosiers
Alexandre Tomberg
Outline
λ-calculus.
 Implementation strategies
 Typed λ-calculus
 Polymorphic types, System F
 Dependent types
 Current work

Motivations

λ-calculus expresses function abstractions
◦ Powerful, Turing-complete.
◦ Foundation of functional programming
languages.
◦ Strong connection with logic.

This project:
◦ Study types
◦ Explore different implementations
The λ-calculus

Very simple language:
◦ Terms M = x | λ x . M | M N
◦ Reductions (λ x . M) N => [N/x] M

Examples
◦ Identity: λ x . x
◦ Constant function: λ x . λ y . x
◦ Omega: λ x . (x x)
Implementation strategies

Explicit substitution
◦ term = var “x” | lam “x” M | app M N
◦ “Natural way” of representing lambda
expressions.
◦ Problems :
 No immediate α-renaming
 Free variable capture problem
Implementation strategies (cont’d)

De Bruijn indices
◦ term M = Var n | lam M | app M N
◦ e.g. Identity: λ . 1, Constant: λ . λ . 2
◦ Advantages:
 Unique representation
◦ Problems:
 Not simple to manipulate.
λ x . (λ y . x) x = λ . (λ . 2). 1
 Need to shift indices in substitution to avoid
capture.
Implementation strategies (cont’d)

Higher Order Abstract Syntax (HOAS)
◦ Use the features of the meta-language !
◦ term M = lam f | app M N
where f is a function term → term
◦ Term substitution :
sub (lam f) N => lam (f N)
◦ α-renaming already provided!
◦ WARNING : Leads to ridiculously short
implementations.
Our implementations
De Bruijn in SML
HOAS in SML
datatype exp = var of int | lam of exp | app of exp * exp
datatype exp = lam of (exp -> exp) | app of exp * exp;
fun shift (var x) l b = if x > b then var (x + l) else var x
fun eval (lam m) = lam (fn x => eval (m x))
| shift (lam m) l b = lam (shift m l (b + 1))
| shift (app (m, n)) l b = app(shift m l b, shift n l b)
| eval (app (m, n)) =
case eval m of
lam m' => eval (m' n)
fun sub (var y) s x l =
| m' => app (m', n)
if y = x then shift s l 0
(* Substitution *)
else if y > x then var (y-1)
(* Free variable *)
else var y
(* Bound variable *)
| sub (lam m) s x l = lam (sub m s (x + 1) (l + 1))
| sub (app (m, n)) s x l = app (sub m s x l, sub n s x l)
fun eval (var x) = var x
| eval (lam m) = lam (eval m)
| eval (app (m, n)) = case eval m of
lam m' => eval (sub m' n 1 0)
| m' => (app (m', eval n))
Our implementations
Twelf
exp : type.
lam : (exp -> exp) -> exp.
SML
datatype exp = lam of (exp -> exp)
| app of exp * exp;
app : exp -> exp -> exp.
fun eval (lam m) = lam (fn x => eval (m x))
eval : exp -> exp -> type.
eval/lam : eval (lam M) (lam M).
eval/app : eval (app M N) N'
<- eval M (lam M')
<- eval (M' N) N'.
| eval (app (m, n)) =
case eval m of
lam m' => eval (m' n)
| m' => app (m', n)
The ω problem
Consider the function:
ω=λx.(xx)
What happens if we apply it to itself?
ω ω = (λ x . ( x x )) ω => ω ω => ω ω => ...
Evaluation never terminates!
 We say (ω ω) doesn’t have a normal form.
 To solve this problem, we introduce
types.

Types
Each function f has a fixed domain A and
co-domain B (like sets in math).
 types τ = a | τ → σ
where a is a base type (e.g. nat)
 Typing rules

◦ XXXXXXXXXXXXXX
Return of the ω

What happens to ω ?
◦ ω = λ x. xx
ω:α→β
x : α, but also x : α → β
α≠α→β
ω cannot have a valid type!
 Simply-typed lambda calculus is strongly
normalizing.

Implementing types

Much easier in Twelf: use HOAS.
t : type.
a : t.
arrow : t -> t -> t.
%infix right 10 arrow.
exp : type.
lam : (exp -> exp) -> exp.
app : exp -> exp -> exp.
check : exp -> t -> type.
check/lam : check (lam M) (A arrow B)
<- {x:exp} (check x A -> check (M x) B).
check/app : check (app M N) B
<- check M (A arrow B)
<- check N A.

This corresponds to Curry style.
Examples

Type checking:
◦ check (lam ([x] x)) (A arrow A)

Secret weapon: we can use Twelf to do
type inference !
◦ %query 1 * check (lam ([x] x)) T
 T = X1 arrow X2 arrow X1.

ω?
◦ %query 1 * check (lam ([x] app x x)) T
 Query error -- wrong number of solutions:
expected 1 in * tries, but found 0
Another implementation

Cute trick :
t : type.
a : t.
arrow : t -> t -> t.
%infix right 10 arrow.
exp : t -> type.
lam : (exp T1 -> exp T2) -> exp (T1 arrow T2).
app : exp (T1 arrow T2) -> exp T1 -> exp T2.
Then ill-typed terms cannot even be
constructed !
 This corresponds to Church style.

Limitations of simple types
Decidable, but no longer Turing complete.
 id = λ x : a . x
id = λ x : b . x
id = λ x : nat . x
id = λ x : (a → a) . x
 We need to write a different identity
function for each type.

◦ To avoid that we need polymorphism.
Polymorphism: System F
Idea: Allow abstraction over types.
 Then, we can write a single identity
function for any type a:
Λa.λx:a.x
 We extend our definition of types and
terms:

◦ τ =a|τ→τ|Πa.τ
◦ M = x | λ x : τ . M | M N | Λ a . M | M {τ}

Implementations follow the same idea.
Dependent types : LF
Idea: Let types depend on values.
 Keep information about our types.
 Examples:

◦ Vector : Nat → type.
◦ Vector n is the type of vectors of length n.
◦ Exp t

Advantages:
◦ We won’t need to prove properties about
our programs !
Current work
Polymorphic types in Twelf
 Dependent types in Twelf
 Types in SML

◦ Problems with HOAS.
Problems with HOAS

Current functional languages (like SML)
present a few problems:
◦ Do not allow us to look inside functions.
◦ Hard to generate λ terms.

Solutions:
◦ Improve functional programming languages.
 Beluga
◦ Try different paradigms.
 Python
Why Python?
Dynamically typed.
 Very flexible code generation.
 User-friendly interface.

I = Lam(lambda x : x)
K = Lam(lambda x : Lam (lambda y : x))
O = Lam(lambda x : App(x, x))
print "I = ", I
print "K = ", K
print "O = ", O
>>>
I =
K =
O =
Lam u. (u)
Lam w. (Lam v. (w))
Lam y. ((y)(y))
Conclusions and future work
Many type systems.
 Various implementations.
 Besides implementing, our
goals include:

◦ Proving equivalence of
representations.