Personal tools

Avoiding partial functions

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(fromJust)
(fromJust)
 
(5 intermediate revisions by 2 users not shown)
Line 11: Line 11:
 
You should replace
 
You should replace
 
<haskell>
 
<haskell>
if isJust mx
+
if isNothing mx
 
then g
 
then g
 
else h (fromJust mx)
 
else h (fromJust mx)
Line 20: Line 20:
 
Nothing -> g
 
Nothing -> g
 
Just x -> h x
 
Just x -> h x
  +
</haskell>
  +
which is equivalent to
  +
<haskell>
  +
maybe g h mx
 
</haskell>
 
</haskell>
   
Line 62: Line 66:
 
forcePair ~(a,b) = (a,b)
 
forcePair ~(a,b) = (a,b)
 
</haskell>
 
</haskell>
  +
  +
You might define <hask>viewRTotal</hask> in a <code>Utility</code> module.
  +
You can import <hask>forcePair</hask> from [http://hackage.haskell.org/packages/archive/utility-ht/0.0.8/doc/html/Data-Tuple-HT.html utility-ht].
   
 
== (!!) ==
 
== (!!) ==
Line 96: Line 103:
 
(_,[]) -> y
 
(_,[]) -> y
 
</haskell>
 
</haskell>
  +
  +
  +
== minimum ==
  +
  +
The function <hask>isLowerLimit</hask> checks if a number is a lower limit to a sequence.
  +
You may implement it with the partial function <hask>minimum</hask>.
  +
<haskell>
  +
isLowerLimit :: Ord a => a -> [a] -> Bool
  +
isLowerLimit x ys = x <= minimum ys
  +
</haskell>
  +
It fails if <hask>ys</hask> is empty or infinite.
  +
  +
You should replace it by
  +
<haskell>
  +
isLowerLimit x = all (x<=)
  +
</haskell>
  +
This definition terminates for infinite lists, if <hask>x</hask> is not a lower limit. It aborts immediately if an element is found which is below <hask>x</hask>.
  +
Thus it is also faster for finite lists.
  +
Even more: It also works for empty lists.
  +
  +
  +
[[Category:Tutorials]]
  +
[[Category:Style]]
  +
[[Category:Idioms]]

Latest revision as of 22:12, 3 August 2012

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.

Contents

[edit] 1 fromJust

You should replace

if isNothing mx
  then g
  else h (fromJust mx)

by

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

which is equivalent to

maybe g h mx

[edit] 2 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

[edit] 3 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 might define
viewRTotal
in a Utility module. You can import
forcePair
from utility-ht.

[edit] 4 (!!)

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.


[edit] 5 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


[edit] 6 minimum

The function
isLowerLimit
checks if a number is a lower limit to a sequence. You may implement it with the partial function
minimum
.
isLowerLimit :: Ord a => a -> [a] -> Bool
isLowerLimit x ys = x <= minimum ys
It fails if
ys
is empty or infinite.

You should replace it by

isLowerLimit x = all (x<=)
This definition terminates for infinite lists, if
x
is not a lower limit. It aborts immediately if an element is found which is below
x
.

Thus it is also faster for finite lists. Even more: It also works for empty lists.