packages feed

liquidhaskell-0.8.2.0: docs/blog/2013-12-01-getting-to-the-bottom.lhs

---
layout: post
title: "Getting To the Bottom of The Lies"
date: 2013-12-01 16:12
comments: true
external-url:
categories: termination
author: Ranjit Jhala, Niki Vazou
published: true
demo: BasicTermination.hs
---

[Previously][ref-lies], we caught LiquidHaskell telling a lie. Today, lets try to
get to the bottom of this mendacity, in order to understand how we can ensure
that it always tells the truth.

<!-- more -->

\begin{code}
module GettingToTheBottom where
\end{code}
The Truth Lies At the Bottom
----------------------------

To figure out how we might prevent such mendacity, lets try to understand 
whats really going on. We need to go back to the beginning.

\begin{code} Recall that the refinement type:
{v:Int | 0 <= v}
\end{code}

is supposed to denote the set of `Int` values that are greater than `0`.

\begin{code} Consider a function:
fib :: {n:Int | 0 <= n} -> {v:Int | 0 <= v}
fib n = e
\end{code}

Intuitively, the type signature states that when checking the body `e` 
we can **assume** that `0 <= n`. 

This is indeed the case with **strict** evaluation, as we are guaranteed 
that `n` will be evaluated before `e`. Thus, either:

1. `n` diverges and so we don't care about `e` as we won't evaluate it, or,
2. `n` is a non-negative value.

Thus, either way, `e` is only evaluated in a context where `0 <= n`.

But of course, this is not the case with **lazy** evaluation, as we may 
well start evaluating `e` without evaluating `n`. Indeed, we may *finish*
evaluating `e` without evaluating `n`. 

Now of course, we know that *if* `n` is evaluated, it will yield a 
non-negative value, but if it is not (or does not) evaluate to a
value, we **cannot assume** that the rest of the computation is dead 
(as with eager evaluation). 

\begin{code} Thus, really with lazy evaluation, the refinement type `{n:Int | 0 <= n}` *actually* means:
(n = _|_) || (0 <= n)
\end{code}

Keeping LiquidHaskell Honest
----------------------------

One approach to forcing LiquidHaskell to telling the truth is to force 
it to *always* split cases and reason about `_|_`.

\begin{code} Lets revisit `explode`
explode = let z = 0
          in  (\x -> 2013 `divide` z) (foo z)
\end{code}

\begin{code}This prevents the cheerful but bogus prognosis that `explode` above was safe, because the SMT solver cannot prove that at the call to `divide` 
    z == 0 && (x = _|_ || (x >= 0 && x < z))  => z /= 0
\end{code}

PIC: PESSIMISTIC-ALWAYS-NO

But alas, this cure is worse than the disease. Effectively it would end up
lobotomizing LiquidHaskell making it unable to prove even trivial things like:

\begin{code}_
{-@ trivial    :: x:Int -> y:Int -> {pf: () | x < y} -> Int @-}
trivial x y pf = liquidAssert (x < y) 10
\end{code}

\begin{code}as the corresponding SMT query
    (pf = _|_ || x < y) => (x < y)
\end{code}

is, thanks to the pesky `_|_`, not valid. 

Terminating The Bottom
----------------------

Thus, to make LiquidHaskell tell the truth while also not just pessimistically 
rejecting perfectly good programs, we need a way to get rid of the `_|_`. That 
is, we require a means of teaching LiquidHaskell to determine when a value
is *definitely* not bottom. 

In other words, we need to teach LiquidHaskell how to prove that a computation 
definitely terminates.