Programming Language Design 2

Reduction and Evaluation


Learning Objectives

  • You know what a value is in lambda calculus.
  • You know the evaluation rules of lambda calculus, and 𝛽-reduction.
  • You can extend lambda calculus with simple constructions such as let expressions.

Here, we focus on the heart of lambda calculus called reduction. Specifically, we talk about 𝛽-reduction, which encodes computation.

Other reductions have also been studied. We briefly mentioned 𝛼-reduction in the previous chapter, but 𝛼-reduction is not used for the dynamics of programming languages, instead, 𝛽-reduction is.

Reduction

Reduction is a process which turns lambda terms to other lambda terms maintaining the meaning of the term in the process. 𝛽-reduction is the process of reducing a lambda term step by step towards some value.

Values

The concept of a value is different from part 4, where values had a distinct data type. Instead, in lambda calculus, being a value is just a property of a term. For simplicity, we consider that only an abstraction is a value of lambda calculus, whether or not its body is a value (or even whether it’s complete or not).

Definition 6.4.1: ValueThe set of values ValueTerm is defined as {𝑡:Term|is_value𝑡},where is_value is defined as follows.is_value:Term{0,1}is_valueAbs(var:_,body:_)=1is_value_=0We often refer to values using the variable name 𝑣.

Let’s now invent a notation for 𝛽-reduction. If the term 𝑡1 reduces to the term 𝑡2 we write 𝑡1𝑡2.

Here are some examples for 𝛽-reduction of lambda terms.

  1. (𝜆𝑥.𝑥)idid
  2. ((𝜆𝑥.𝜆𝑦.𝑦)id)id(𝜆𝑦.𝑦)idid where id=𝜆𝑥.𝑥

We also decide that variables should not reduce to anything, and instead should lead to an error. This is because only complete programs should be evaluated in the first place as complete programs remain complete under 𝛽-reduction.

We will use the informal lambda calculus notation, but the reader is expected to understand it as just “syntax sugar” for the formal lambda terms defined earlier.

Here is a reminder of the formal definition of lambda terms:

TermVar(String)|Abs(var:String,body:Term)|App(Term,Term)

As with any relation, we can express 𝛽-reduction using a set of inference rules. 𝛽-reduction is defined as the smallest closure of the following rules:

𝑡1𝑡1App1𝑡1 𝑡2𝑡1 𝑡2𝑡2𝑡2is_value(𝑣1)App2𝑣1 𝑡2𝑣1 𝑡2is_value(𝑣2)AppAbs(𝜆𝑥.𝑡1) 𝑣2[𝑥𝑣2]𝑡1

As usual, we tacitly assume that 𝑡1,𝑡1,𝑡2,𝑡2,𝑣1,𝑣2 are lambda terms and 𝑥 is a string.

The purpose of the App1 rule is to reduce the “function term” until it is a value, and App2 reduces the argument until it’s a value. Once the function term has been (hopefully) reduced to an abstraction and the argument to a value, AppAbs substitutes the variable parameter by the value 𝑣2 inside the function body 𝑡1. These rules a written in a way that ensures there’s at most one term that a term can reduce to. Notably this requires that values don’t evaluate to anything, because otherwise App1 could be used with 𝑡1 and 𝑡1 the same value.

𝑡,¬is_value𝑡! 𝑡,𝑡𝑡

The notation ! stands for exists unique.

Definition 6.4.2: 𝛽-reductionThe evaluation relation () between two terms is the smallest relationsuch that the following holds.Let 𝑡1,𝑡1,𝑡2,𝑡2,𝑣1,𝑣2 be lambda terms and 𝑥 be a string.1.If 𝑡1𝑡1, then 𝑡1𝑡2𝑡1𝑡2.2.If 𝑣1 value, and 𝑡2𝑡2, then 𝑡1𝑡2𝑡1𝑡2.3.If 𝑣2 value, then (𝜆𝑥.𝑡1) 𝑣2[𝑥𝑣2]𝑡1.Where substitution ([𝑥𝑣]𝑡) is defined as in Definition 6.3.5.

Note that we give no rules for reducing values. This is because a term should not be reduced after it has reached its final value.

Loading Exercise...
Loading Exercise...
Loading Exercise...
Loading Exercise...

Small-step Semantics

The semantics, or more precisely, the dynamics of lambda calculus are described in small-step style, which means that evaluation works one step at a time by doing 𝛽-reduction repeatedly.

Using the 𝛽-reduction defined above (along with substitution), we are ready to implement the step function. This is the topic of the next exercise.

Loading Exercise...

Multi-Step Evaluation

Definition 6.4.3: Multi-Step RelationDefine 𝑡𝑣 to denote that the Term 𝑡 reduces to the value 𝑣 in somenumber of steps.𝑡𝑡𝑡𝑣

The big-step evaluation 𝑡𝑣 is typically equivalent to 𝑡𝑣.

Next, implement multistep which applies step repeatedly until reaching a value.

Loading Exercise...

Base Types

So far, we haven’t seen any base types, such as integers or booleans in the lambda calculus, so let’s add them next.

In theory, the theory of integers and booleans is expressible in plain untyped lambda calculus with simply adding syntax sugar for representing them. However, like parsing directly to AST, we can just work and reason with the sugared language and skip the translation/lowering process entirely. This way we need to write more code for type checking and evaluation, but the features of the language are immediately expressible without encoding and decoding. Also then the evaluated terms actually resemble booleans etc. rather than some hard-to-read encoded versions making a REPL for the language more usable.

We will not actually implement booleans in this chapter, but in the next chapter instead.

Let Expressions

Let’s add let expressions, like let𝑥=𝑦in𝑥, to the language.

A note about the syntax: changing in to be ; or even just a new line would make the syntax look much more imperative while still being a functional language. Consider trying different variations in your own project.

First, we need to extend the set (type) of terms to include let expressions:

Term|Let(var:String,val_t:Term,body:Term)

The name val_t is called the “value” assigned to the variable var, even though it might not actually be a value and is reducible. The evaluation needs to reduce it to a value before substituting in the body.

Now, substitution needs to be changed. Let expressions should have similar semantics as abstractions, so we only substitute in the body if the variables don’t match.

subst:(String,Term,Term)Termsubst𝑥 𝑣Var(𝑥)=𝑣subst𝑥 𝑣Var(𝑦)=Var(𝑦),if𝑥𝑦subst𝑥 𝑣App(𝑡1,𝑡2)=App(subst(𝑥,𝑣,𝑡1),subst(𝑥,𝑣,𝑡2))subst𝑥 𝑣Abs(𝑥,𝑇,𝑡)=Abs(𝑥,𝑇,𝑡)subst𝑥 𝑣Abs(𝑦,𝑇,𝑡)=Abs(𝑦,𝑇,subst(𝑥,𝑣,𝑡))if𝑥𝑦subst𝑥 𝑣Let(𝑥,𝑡1,𝑡2)=Let(𝑥,subst(𝑥,𝑣,𝑡1),𝑡2)subst𝑥 𝑣Let(𝑦,𝑡1,𝑡2)=Let(𝑦,subst(𝑥,𝑣,𝑡1),subst(𝑥,𝑣,𝑡2))if𝑥𝑦

Also, we consider let expressions not values as intuitively we know they can always be reduced.

Evaluation

Evaluation of let expressions works by first reducing the val_t to a value, and then substituting the variable var by that value in the body.

This can be expressed using the following rules:

𝑡1𝑡1Let1let𝑥=𝑡1in𝑡let𝑥=𝑡1in𝑡is_value(𝑣1)Letlet𝑥=𝑣1in𝑡[𝑥𝑣1]𝑡
Loading Exercise...