The ChasingBottoms package

Do you ever feel the need to test code involving bottoms (e.g. calls to
the error function), or code involving infinite values? Then this
library could be useful for you.

It is usually easy to get a grip on bottoms by showing a value and
waiting to see how much gets printed before the first exception is
encountered. However, that quickly gets tiresome and is hard to automate
using e.g. QuickCheck
(http://www.cse.chalmers.se/~rjmh/QuickCheck/). With this library you
can do the tests as simply as the following examples show.

Testing explicitly for bottoms:

> isBottom (head [])

True

> isBottom bottom

True

> isBottom (\_ -> bottom)

False

> isBottom (bottom, bottom)

False

Comparing finite, partial values:

> ((bottom, 3) :: (Bool, Int)) ==! (bottom, 2+5-4)

True

> ((bottom, bottom) :: (Bool, Int)) <! (bottom, 8)

True

Showing partial and infinite values (\/! is join and /\! is meet):

> approxShow 4 $ (True, bottom) \/! (bottom, 'b')

"Just (True, 'b')"

> approxShow 4 $ (True, bottom) /\! (bottom, 'b')

"(_|_, _|_)"

> approxShow 4 $ ([1..] :: [Int])

"[1, 2, 3, _"

> approxShow 4 $ (cycle [bottom] :: [Bool])

"[_|_, _|_, _|_, _"

Approximately comparing infinite, partial values:

> approx 100 [2,4..] ==! approx 100 (filter even [1..] :: [Int])

True

> approx 100 [2,4..] /=! approx 100 (filter even [bottom..] :: [Int])

True

The code above relies on the fact that bottom, just as error
"...", undefined and pattern match failures, yield
exceptions. Sometimes we are dealing with properly non-terminating
computations, such as the following example, and then it can be nice to
be able to apply a time-out:

> timeOut' 1 (reverse [1..5])

Value [5,4,3,2,1]

> timeOut' 1 (reverse [1..])

NonTermination

The time-out functionality can be used to treat "slow" computations as
bottoms:

The code has been tested using GHC. Most parts can probably be
ported to other Haskell compilers, but this would require some work.
The TimeOut functions require preemptive scheduling, and most of
the rest requires Data.Generics; isBottom only requires
exceptions, though.