Avoiding partial functions
From HaskellWiki
(Difference between revisions)
(first examples: head, tail, !!) |
(→fromJust) |
||
| (10 intermediate revisions not shown.) | |||
| Line 6: | Line 6: | ||
Whereever we write "check whether x is in the domain of f before computing f x", | 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. | we replace it by combination of check and computation of f. | ||
| + | |||
| + | == fromJust == | ||
| + | |||
| + | You should replace | ||
| + | <haskell> | ||
| + | if isNothing mx | ||
| + | then g | ||
| + | else h (fromJust mx) | ||
| + | </haskell> | ||
| + | by | ||
| + | <haskell> | ||
| + | case mx of | ||
| + | Nothing -> g | ||
| + | Just x -> h x | ||
| + | </haskell> | ||
| + | which is equivalent to | ||
| + | <haskell> | ||
| + | maybe g h mx | ||
| + | </haskell> | ||
== head, tail == | == head, tail == | ||
| Line 22: | Line 41: | ||
</haskell> | </haskell> | ||
| + | == init, last == | ||
| + | |||
| + | You may replace | ||
| + | <haskell> | ||
| + | if null xs | ||
| + | then g | ||
| + | else h (init xs) (last xs) | ||
| + | </haskell> | ||
| + | by | ||
| + | <haskell> | ||
| + | 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) | ||
| + | </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 39: | Line 86: | ||
This is also more lazy, since for computation of <hask>length</hask> you have to visit every element of the list. | This is also more lazy, since for computation of <hask>length</hask> you have to visit every element of the list. | ||
| + | |||
| + | |||
| + | == irrefutable pattern match on (:) == | ||
| + | |||
| + | You should replace | ||
| + | <haskell> | ||
| + | if k < length xs | ||
| + | then let (prefix,x:suffix) = splitAt k xs | ||
| + | in g prefix x suffix | ||
| + | else y | ||
| + | </haskell> | ||
| + | by | ||
| + | <haskell> | ||
| + | case splitAt k xs of | ||
| + | (prefix,x:suffix) -> g prefix x suffix | ||
| + | (_,[]) -> y | ||
| + | </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]] | ||
Current revision
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 |
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 mx2 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
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)
viewRTotal
Utility module.
You can import forcePair
4 (!!)
You should replace
if k < length xs then xs!!k else y
by
case drop k xs of x:_ -> x [] -> y
length
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
6 minimum
The functionisLowerLimit
minimum
isLowerLimit :: Ord a => a -> [a] -> Bool isLowerLimit x ys = x <= minimum ys
ys
You should replace it by
isLowerLimit x = all (x<=)
x
x
Thus it is also faster for finite lists. Even more: It also works for empty lists.
Categories: Tutorials | Style | Idioms
