Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

foldl Implementation with Runtime Errors

Tags:

haskell

Learn You a Haskell explains foldl1:

The foldl1 and foldr1 functions work much like foldl and foldr, only you don't need to provide them with an explicit starting value. They assume the first (or last) element of the list to be the starting value and then start the fold with the element next to it. ...

Because they depend on the lists they fold up having at least one element, they cause runtime errors if called with empty lists

I figured its implementation is, more or less, the following:

foldl1' :: (a -> a -> a) -> [a] -> a
foldl1' f ys = foldl f (head ys) (tail ys)

But, this potential run-time error troubles me.

Why not implement foldlOption in the following way?

foldlOption :: (a -> a -> a) -> [a] -> Maybe a
foldlOption f [] = Nothing
foldlOption f ys = Just (foldl f (head ys) (tail ys))

REPL

*Main> foldlOption (\acc elem -> if (elem > acc) then elem else acc) []
Nothing

-- find max
*Main> foldlOption (\acc elem -> if (elem > acc) then elem else acc) [1,100,2,3]
Just 100

EDITED

Updated foldl1's and foldlOption's definitions to use tail ys as the last argument to foldl, not ys per Lee Duhem's correction. .

like image 270
Kevin Meredith Avatar asked Apr 20 '14 15:04

Kevin Meredith


1 Answers

There's actually no good reason why not to do this. Many of the functions in Haskell's prelude like head, tail, init, and many many others fail unnecessarily.

It would be much nicer for them to explicitly note their failure in the types, but that's unfortunately just not what happened when Prelude was standardized and we can't very well change several core functions like head!

Nowadays I recommend simply not using many of these functions and opting for pattern matching, or Gabriel Gonzalez's errors library which provides alternate versions of prelude's partial functions which fail properly.

For example in Control.Error.Safe there's

foldl1Err :: e -> (a -> a -> a) -> [a] -> Either e a

and errors also exports safe, a similar library with Maybe's which has the function

foldl1May :: (a -> a -> a) -> [a] -> Maybe a

exactly like what you wanted :)

like image 95
Daniel Gratzer Avatar answered Sep 17 '22 22:09

Daniel Gratzer