Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect bottom value in Haskell

I've written a haskell function which splits a list xs into (init xs, last xs) like so:

split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, x)
        split' acc (x:xs) = split' (x:acc) xs

Since an empty list can not be split in this way, there is no match for the empty list. However, I did not want to simply error ... the function. Thus I defined the following:

split [] = ([], undefined)

Thanks to lazy evaluation I can thus define a safe init which simply returns the empty list for the empty list:

init' = fst . split

Is there some way how I could detect the undefined if I tried to access it, such that

last' xs
  | isUndefined (snd xs) = ...
  | otherwise            = ...

I do know about Maybe and Either, and that those are a better choice for expressing what I want. However I wondered if there is a way to detect an actual value of undefined, i.e. in terms of catching errors, like catching exceptions.

like image 265
scravy Avatar asked Feb 22 '12 09:02

scravy


4 Answers

undefined is no better than using error. In fact, undefined in Prelude is defined as

undefined =  error "Prelude.undefined"


Now, a function that can't result in an error is called a "total function", i.e. it is valid for all input values.

The split function you've currently implemented has the signature

split :: [a] -> ([a], a)

This is a problem, since the type signature promises that the result always contains a list and an element, which is clearly impossible to provide for empty lists of generic type.

The canonical way in Haskell to address this is to change the type signature to signify that sometimes we don't have a valid value for the second item.

split :: [a] -> ([a], Maybe a)

Now you can write a proper implementation for the case where you get an empty list

split [] = ([], Nothing)
split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, Just x)
        split' acc (x:xs) = split' (x:acc) xs

Now you can detect the missing value case by pattern-matching

let (init', last') = split xs
in case last' of
    Nothing -> ... -- do something if we don't have a value
    Just x  -> ... -- do something with value x
like image 70
shang Avatar answered Nov 05 '22 12:11

shang


Because bottom subsumes non-termination, the function isUndefined would have to solve the halting problem and thus cannot exist.

But note that even if it existed, you still could not tell if the undefined value in the 2nd element of your tuple was put there through your split function or if the last element of the list was already undefined.

like image 42
Ingo Avatar answered Nov 05 '22 12:11

Ingo


The error function doesn't do anything until it is evaluated, so you can do something like:

split [] = ([], error "split: empty list")

last' = snd . split
like image 8
Sjoerd Visscher Avatar answered Nov 05 '22 12:11

Sjoerd Visscher


From the Haskell 2010 Language Report > Introduction # Values and Types

Errors in Haskell are semantically equivalent to ⊥ (“bottom”). Technically, they are indistinguishable from nontermination, so the language includes no mechanism for detecting or acting upon errors.

To be clear, undefined is intended to be a way to insert ⊥ into your program, and given that (as shang noted) undefined is defined in terms of error, there is, therefore, "no mechanism for detecting or acting upon undefined".

like image 4
Dan Burton Avatar answered Nov 05 '22 10:11

Dan Burton