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.
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
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.
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
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
".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With