Lecture 4: (Non-)termination

Jesper Cockx

3 September 2019

"I’ll save the world…

tomorrow."

– unknown

What happens if we have a partial function in Agda?

- Theory would become inconsistent!

```
absurd : ⊥
absurd = absurd
```

- Typechecker would crash or loop!

```
f : ℕ → ℕ
f zero = 42
test : Vec ℕ (f 1)
test = []
```

⇒ Partial functions must be ruled out!

**Completeness**of pattern matching**Structural recursion**of recursive functions**Strict positivity**of inductive datatypes**Consistency**of universe levels

2-4 together ensure normalization of well-typed terms

plus : ℕ → ℕ → ℕ plus zero m = m plus (suc n) m = suc (plus n m) natEq : ℕ → ℕ → Bool natEq zero zero = true natEq zero (suc m) = false natEq (suc n) zero = false natEq (suc n) (suc m) = natEq n m

fib : ℕ → ℕ fib zero = zero fib (suc zero) = suc zero fib (suc (suc n)) = plus (fib n) (fib (suc n))

ack : ℕ → ℕ → ℕ ack zero m = suc m ack (suc n) zero = ack n (suc zero) ack (suc n) (suc m) = ack n (ack (suc n) m)

“If all functions in Agda are total, doesn’t that mean Agda is not Turing-complete?”

*Answer*: **NO!** Agda just forces you to be *honest* about when a function is non-terminating.

A **coinductive type** = a type with possibly infinitely deep values.

record Stream (A : Set) : Set where coinductive field head : A tail : Stream A open Stream repeat : {A : Set} → A → Stream A head (repeat x) = x tail (repeat x) = repeat x

mutual record Coℕ′ : Set where coinductive field force : Coℕ data Coℕ : Set where zero : Coℕ suc : Coℕ′ → Coℕ open Coℕ′ public

fromℕ : ℕ → Coℕ fromℕ′ : ℕ → Coℕ′ fromℕ zero = zero fromℕ (suc x) = suc (fromℕ′ x) fromℕ′ x .force = fromℕ x infty : Coℕ infty′ : Coℕ′ infty = suc infty′ infty′ .force = infty

Remember: all Agda functions must be **total**

One way to work around this is by adding ‘fuel’:

step : Term → Term ⊎ Val step = ⋯ eval : ℕ → Term → Maybe Val eval (suc n) t = case (step t) of λ where (inj₁ t') → eval n t (inj₂ v) → just v eval zero t = nothing

Can we do better?

`Delay`

monadA value of type `Delay A`

is

- either a value of type
`A`

produced**now** - or a computation of type
`Delay A`

producing a value**later**

The `Delay`

monad captures the effect of *non-termination*

mutual record Delay (A : Set) : Set where coinductive field force : Delay' A data Delay' (A : Set) : Set where now : A → Delay' A later : Delay A → Delay' A open Delay public never : {A : Set} → Delay A force never = later never

`Delay`

ed valueunroll : {A : Set} → ℕ → Delay A → A ⊎ Delay A unroll zero x = inj₂ x unroll (suc n) x = case (force x) of λ where (now v ) → inj₁ v (later d) → unroll n d

Totality requirement: coinductive definitions should be **productive**: computing each observation should be terminating.

To ensure this, Agda checks that corecursive calls are **guarded by constructors**, but this is often quite limiting.

A more flexible and modular approach is to use **sized types**.

`Size`

`Size`

≃ abstract version of the natural numbers extended with `∞`

For each `i : Size`

, we have a type `Size< i`

of sizes **smaller than i**.

**Note**: pattern matching on `Size`

is not allowed!

mutual record Delay (i : Size) (A : Set) : Set where coinductive constructor delay field force : {j : Size< i} → Delay' j A data Delay' (i : Size) (A : Set) : Set where return' : A → Delay' i A later' : Delay i A → Delay' i A

`i`

≃ how many more steps are we allowed to observe.

`Delay ∞ A`

is the type of computations that can take *any* number of steps.

WHILE statements can have two effects:

- Modify the environment ⇒
`State`

monad - Go into a loop ⇒
`Delay`

monad

We combine both effects in the `Exec`

monad.

`Exec`

monadrecord Exec {Γ : Cxt} (i : Size) (A : Set) : Set where field runExec : (ρ : Env Γ) → Delay i (A × Env Γ) open Exec public execStm : ∀ {Γ} {i} (s : Stm Γ) → Exec {Γ} i ⊤ execStm = ⋯ execPrg : ∀ {i} (prg : Program) → Delay i ℤ execPrg prg = ⋯

See V3/Interpreter.agda for full code.

Now you should be ready to add a bigger new feature:

- A new control operator:
`if`

statements,`do/while`

loops,`for`

,`switch`

, … - New types:
`char`

,`bool`

, … - New programming concepts: function calls, pointers (hard!), …

Extend the syntax, the typechecker, and the interpreter with rules for your new feature.