# Lecture 10: Functional Programming Languages

Programming Languages Course
Aarne Ranta (aarne@chalmers.se)

Book: 6.5, 7.3

## Plan

Programming with states vs. values

Functions as values

Anonymous functions

Lambda calculus

Evaluation strategies

Simply typed lambda calculus

Polymorphic functions and principal types

## Imperative vs. functional programming

Imperative (aka. procedural): the execution of a program is both evaluation of expressions and changing a state (values of variables).

• "stateful" programming
• evaluation can have side effects

Functional: the execution of a program is just evaluation of expressions, without changing the values of variables.

• "stateless" programming
• no side effects

States and side effects have a double effect:

1. enable efficient time and space consumption
2. make it harder to reason about the program and therefore to optimize it

In the past, (1) dominated. Currently (2) is getting more important.

In fact, a powerful optimization technique is single static assignments, where all variables are only given value once.

## The structure of a functional program

The levels are:

• modules
• definitions
• expressions

Thus no statements!

Example (could be written in a subset of Haskell):

```    doub x    = x + x

twice f x = f (f x)

main      = print (twice quadruple 2)
```

## Functions in imperative languages

So, is functional programming less expressive than imperative?

Sure we can write functions in C/C++/Java, too:

```    // doub x    = x + x

int doub (int x)
{
return x + x ;
}
```

But this is what is called a first-order function: the arguments are objects like numbers and class objects - but not themselves functions.

In C++, it is also possible to write second-order functions, which take functions as arguments:

```    // twice f x = f (f x)

int twice(int f (int n), int x)
{
return f(f(x)) ;
}
```

## Functions as first-class citizens

In a functional language, a functions are first-class citizens.

This means: a function has a value even if it is not applied.

This is not quite so in C++: you cannot return a function as a value:

```    // quadruple = twice doub

/* not possible

(int f (int x)) quadruple(int x)
{
return twice(doub) ;
}
*/

{
return twice(doub, x) ;
}
```

## Function types and partial application

The types of functions in Haskell are written in this way:

```    max : Int -> Int -> Int
```

The notation is right-associative, and hence equivalent to

```    max : Int -> (Int -> Int)
```

Thus, a "two-place function" is really a one-place function whose value is also a function. (Haskell uses `::` for typing, but we stick to `:`)

The typing rule for functions is:

```    Env => f : A -> B    Env => a : A
---------------------------------
Env => f a : B
```

Thus partial application is meaningful:

```    max 4 :: Int -> Int -- returns the greater one of its argument and 4
```

## Functions of tuples, currying

In many other languages, the value of a function must be a "basic type", i.e. not a function type.

This corresponds in Haskell to having a tuple of arguments:

```    maxt : (Int,Int) -> Int
```

The typing rule for tuples is

```    Env => a : A   Env => b : B
---------------------------
Env => (a,b) : (A,B)
```

Partial application is thus not meaningful: you have to form the tuple first.

The move

```    (A,B) -> C    ==>    A -> B -> C
```

is called currying, with reference to Haskell B. Curry.

At the same time as it is a powerful programming technique, it simplifies the semantics and implementation of programming languages.

## Anonymous functions

In a functional language, you can form anonymous functions by using lambda abstraction:

```    timesNine = twice (\x -> x + x + x)
```

In C++, you would have to write a named function for tripling:

```    // triple x = x + x + x
int triple(int x)
{
return x + x + x ;
}

// timesNine = twice triple
int timesNine(int x)
{
return twice(triple, x) ;
}
```

There is a recent Lambda Library for C++ permitting this.

## Desugaring function definitions

There is no difference in writing a function definition in these three ways:

```    foo x y = x + y * y

foo x = \y -> x + y * y

foo = \x -> \y -> x + y * y
```

We can thus simplify the abstract syntax of programs by saying that definitions have the form

```    Def ::= Ident "=" Exp
```

and treating the other form

```    Def ::= Ident [Ident] "=" Exp
```

as syntactic sugar:

```    id x1 ... xn = exp   ===>   id = \x1 -> ... \xn -> exp
```

In this way, the environment is very simple: just mapping identifiers to expressions!

## Evaluating expressions

Now it is enough to work with the following tiny language:

```    Exp ::= Ident | Integer | "(" Exp Exp ")" | "(" "\" Ident "->" Exp ")"
```

Thus the expressions are variables, integer constants, applications, and lambda abstractions. (Operations like `+` can be treated as functions.)

For simplicity, we define the values to be expressions (notice that values are either integers or functions).

The big-step semantics is:

```    env => var ⇩ lookup env var

env => const ⇩ const

env => fun ⇩ (\x -> body)   env => arg ⇩ val   env => body(val/x) ⇩ result
--------------------------------------------------------------------------------
env => (fun  arg) ⇩ result

env => (\x -> body) ⇩ (\x -> body)
```

Thus lambda abstractions are not evaluated when alone, but only when they are applied to arguments.

The operation `body(val/x)` is substitution: replace all occurrences of the variable `x` by the expression `val` in the expression `body`.

## Substitution

Example:

```    (x + x + 3)(5/x)  ⇩  5 + 5 + 3
```

Definition, by syntax-directed translation

```    const(val/x) = const

var(val/x) = val, if var == x
var(val/x) = var, if var != x

(fun arg)(val/x) = (fun(val/x) arg(val/x))

(\z -> body)(val/x) = (\z -> body(val/x)), if no capture
```

For abstraction, we need a condition to prevent capture. Capture means that some `z` in `val` becomes bound by the lambda.

Example evaluation:

```    twice = \f -> \a -> f (f a)
double = \x -> x + x

((twice double) 3)
= (((\f -> \a -> f (f a)) (\x -> x + x)) 3)
= (((\a -> (\x -> x + x) ((\x -> x + x) a))) 3)
= (((\a -> (\x -> x + x) (a + a))) 3)
= (((\a -> a + a + a + a)) 3)
= 3 + 3 + 3 + 3
```

## Examples of capture

Recall the unconditioned substitution rule:

```    (\z -> body)(val/x) = (\z -> body(val/x))
```

The following go wrong:

```       (\x -> x)(5/x)
= (\x -> x(5/x))  -- should not replace a bound x
= (\x -> 5)

(\y -> x)(y/x)
= (\y -> x(y/x))  -- should not bind a free y
= (\y -> y)
```

Solution: alpha conversion: change the name of a bound variable to a new one that does not appear in the substitution.

```       (\x -> x)(5/x)
= (\z -> z)(5/x)
= (\z -> z(5/x))
= (\z -> z)

(\y -> x)(y/x)
= (\z -> x)(y/x)
= (\z -> x(y/x))
= (\z -> y)
```

## Call by value vs. call by name

Two evaluation strategies.

Call by value (as above): evaluate argument before substitution

```    env => fun ⇩ (\x -> body)    env => arg ⇩ val    env => body(val/x) ⇩ result
----------------------------------------------------------------------------
env => (fun arg) ⇩ result
```

Call by name : substitute first, then evaluate

```    env => fun ⇩ (\x -> body)    env => body(arg/x) ⇩ result
--------------------------------------------------------
env => (fun arg) ⇩ result
```

What difference does it make?

• with call by value, all arguments are always evaluated
• with call by name, some arguments are never evaluated

C, C++, Java, ML use call by value.

Haskell uses a variant of call by name: call by need.

## Termination of evaluation

Consider the code

```    infinite = 1 + infinite

first x y = x

main = first 5 infinite
```

With call by value, we get

```    main
= first 5 infinite
= (\x -> \y -> x) 5 (1 + infinite)
= (\y -> 5) (1 + infinite)
= (\y -> 5) (2 + infinite)
...
```

With call by name,

```    main
= first 5 infinite
= (\x -> \y -> x) 5 infinite
= (\y -> 5) infinite
= 5
```

Generally: if an evaluation can terminate, it terminates with the call by name order. But not necessarily with call by value.

## Lazy evaluation and call by need

Call by name is sometimes called lazy evaluation: don't evaluate until you need!

Disadvantage: you may need to evaluate the same expression twice:

```    doub x = x + x

doub (doub 8)
= doub 8 + doub 8  -- by name
= 8 + 8 + 8 + 8
= 32

doub (doub 8)
= doub 16          -- by value
= 16 + 16
= 32
```

Call by need: change the environment by filling in the value when an expression is evaluated. Next time, the value is found from the environment and not computed again.

## Types for lambda calculus

In simply typed lambda calculus, there is

• a set of basic types, such as `int`
• function types of form `typ1 -> typ2`

The typing rules are

```    env => var : A, if A == lookup env var

env => const : int, for integer constants

env => fun : A -> B    env => arg : A
-------------------------------------
env => (fun  arg) : B

env, x : A => body : B
----------------------------
env => (\x -> body) : A -> B
```

where `A, B` are any types

## Polymorphism

What is the type of

```    \x -> x
```

There are infinitely many types:

```    int                -> int
(int -> int)       -> (int -> int)
(int -> int - int) -> (int -> int -> int)
```

All these types have the form

```    A -> A
```

Conversely, what ever type `A` is, the expression has it.

We say that `\x -> x` is polymorphic: it has many types.

## Polymorphism in C++ and Java

Some polymorphism is possible in C++ using templates.

```    // twice : (A -> A) -> A -> A

template<class A> A twice(A f (A n), A x)
{
return f(f(x)) ;
}
```

Similarly in generic Java (Java 1.5):

```    // id : A -> A

public static <A> A id(A x)
{
return x ;
}
```

(and there are no functions of functions).

Templates in C++ and generics in Java were inspired by ML and Haskell.

## Type inference in functional languages

In general, the result is polymorphic.

There is an algorithm returning the most general type. Thus

```    infer (\x -> x)             = A -> A

infer (\x -> \y -> x)       = A -> B -> A

infer (\f -> \x -> f (f x)) = (A -> A) -> A -> A

infer (\x -> x + x)         = int -> int
```

## An example of type inference

```  infer (\f -> \x -> f (f x)) : t

t = a -> b -> c         ==>  f (f x) : c

f : d -> e              ==>  a = d -> e  because f : a

c = e    because f _ : e

f x : d  because f : d -> _
f x : e  because f : _ -> e
hence d = e

x : d  ==> d = b because x : b

we have

a = d -> e = b -> b
c = d = b

hence

t = (b -> b) -> b -> b
```

Isn't this mind-twisting - a little bit like Sudoku? Can it be made systematic?

## Implementing type inference

The algorithm calls unification: Book 6.5.4

Unification finds solutions to equations with type variables.

Unification algorithm: Book 6.5.5

## Polymorphic typing rules

```    env => var : A', if A == lookup env var and A > A'

env => const : int, for integer constants

env => fun : A -> B    env => arg : A
-------------------------------------
env => (fun arg) : B

env, x : A => body : B
----------------------------
env => (\x -> body) : A -> B
```

Here `A > A'` means that `A'` is an instance of `A`. Examples:

```    a       > Int
a       > Int -> Int
a       > b -> c
a -> b  > Int -> (Int -> b)
```

where `a, b, c` are type variables.

## The unification algorithm

Input: two types with variables.

Output: type substitution s (mapping from variables to types).

Notation: Ts is type T after performing substitution s.

Property: if s = unify T U, then Ts = Us

```    unify a b = {a = b}  -- a,b distinct variables

unify a T = {a = T}  -- T non-variable, a doesn't occur in T

unify (T -> U) (T' -> U') =
s1 := unify T T'
s2 := unify Us1 U's1
return s2 + s1
```

Occurs check: we cannot unify e.g. `a` with `a -> b`.

## Type inference with unification

Input: environment mapping variables to their types, expression.

Output: substitution, type.

```    infer env x = ({}, fresh env (lookup x env))

infer env n = ({}, Int)

infer env (fun arg) =
(s1,T1) := infer env fun
(s2,T2) := infer (env s1) arg
b       := freshVar env
s3      := unify (T1 s2) (T2 -> b)
return (s3 + s2 + s1, b s3)

infer env (\x -> body) =
b       := freshVar env
(s,T)   := infer (env, x : b) body
return (s, (b s) -> T)
```

The auxiliary `freshVar` returns a type variable not yet occurring in env.

## Type inference example revisited

```  infer () (\f -> \x -> f (f x))
infer (f : a, x : b) (f (f x))
({},c) <- infer (f:a, x:b) f
({},d) <- infer (f:a, x:b) x
{c = d -> e} <- unify c (d -> e)
({c = d -> e}, e) <- infer env (f x)
{c = e -> e} <- unify c (e -> e)
({c = d -> e, c = e -> e, d = e}, e) <- infer env (f (f x))
(subst, e -> e) <- infer env (\x -> f (f x))
(subst, (e -> e) -> e -> e) <- infer env (\f -> \x -> f (f x))
```

with some shortcuts and "obvious" abbreviations env and subst... it's better to implement and try out!