As an exercise (scroll to the first set of exercises) for learning Haskell, I have to implement foldl1.

I believe I have implemented it successfully and while there is an answer available, it would be great to have the eye of an expert and more importantly, the thought process of why certain decisions were made.

Below is my code.

foldl1' :: (a -> a -> a) -> [a] -> a
foldl1' f [a] = a
foldl1' f xs =
  foldl1' f ((f (xs !! 0) (xs !! 1)):(drop 2 xs))

First, I like the explicit statement of the type signature. That's a good habit to get into, and makes it easier to capitalise on perhaps the greatest strength of using Haskell which is all the compile time checking. The provided signature is as general as it can be for lists.

Second, the single element base case is written cleanly and correctly.

The recursive case has room for a bit of picking apart. It is convention to use pattern matching syntax x:xs (or x1:x2:xs) for list recursive functions. As well as being cleaner to read, the behaviour is slightly different in that it can work out the first, second, and remainder of the list in a single pass without having to separately call !! twice and drop once.

foldl1' f (x1:x2:xs) = foldl1' f ((f x1 x2):xs)

One other improvement that I would suggest, taken directly from the prelude function by the same name, is explicitely handling the failure case when provided with an empty list. For comparison the inbuilt function produces an exception:

foldl1 (+) []

*** Exception: Prelude.foldl1: empty list

  • 1
    \$\begingroup\$ foldl1' f (a:b:t) = foldl1' f (f a b : t) is even better. :) then, making an internal function which will take accumulator argument and the rest of the list, without passing f around, and without the extraneous :s. \$\endgroup\$
    – Will Ness
    Nov 15 '18 at 7:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.