Avoiding partial functions

From HaskellWiki
Revision as of 12:10, 5 June 2012 by Lemming (talk | contribs) (fromJust)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

There are several partial functions in the Haskell standard library. If you use them, you always risk to end up with an undefined. In this article we give some hints how to avoid them, leading to code that you can be more confident about.

For a partial function f the general pattern is: Whereever we write "check whether x is in the domain of f before computing f x", we replace it by combination of check and computation of f.

fromJust

You should replace

if isJust mx
  then g
  else h (fromJust mx)

by

case mx of
   Nothing -> g
   Just x -> h x

head, tail

You should replace

if null xs
  then g
  else h (head xs) (tail xs)

by

case xs of
   [] -> g
   y:ys -> h y ys

init, last

You may replace

if null xs
  then g
  else h (init xs) (last xs)

by

case xs of
   [] -> g
   y:ys -> uncurry h $ viewRTotal y ys

viewRTotal :: a -> [a] -> ([a], a)
viewRTotal x xs =
   forcePair $
   foldr
      (\x0 go y -> case go y of ~(zs,z) -> (x0:zs,z))
      (\y -> ([],y))
      xs x

forcePair :: (a,b) -> (a,b)
forcePair ~(a,b) = (a,b)

(!!)

You should replace

if k < length xs
  then xs!!k
  else y

by

case drop k xs of
   x:_ -> x
   [] -> y

This is also more lazy, since for computation of length you have to visit every element of the list.


irrefutable pattern match on (:)

You should replace

if k < length xs
  then let (prefix,x:suffix) = splitAt k xs
       in  g prefix x suffix
  else y

by

case splitAt k xs of
   (prefix,x:suffix) -> g prefix x suffix
   (_,[]) -> y