1

I was introduced to the use of fold in defining function. I have an idea how that works but im not sure why one should do it. To me, it feels like just simplifying name of data type and data value ... Would be great if you can show me examples where it is significant to use fold.

data List a = Empty | (:-:) a (List a)

--Define elements
List a :: *
[] :: List a
(:) :: a -> List a  -> List a

foldrList :: (a -> b -> b) -> b -> List a -> b
foldrList f e Empty = e
foldrList f e (x:-:xs) = f x (foldrList f e xs)
5
  • 3
    sum = foldr (+) 0. product = foldr (*) 0. map f = foldr (\x xs -> f x : xs) []. Commented Feb 6, 2019 at 8:04
  • 5
    folding is just, like, a unifying theory of recursion. Instead of writing out your recursive definitions by hand, you write them down as calls to a fold. It's easier, less error prone, less to type, more opportunities for functional composition. Commented Feb 6, 2019 at 8:20
  • 2
    I would say the biggest benefit is in readability. Developers (with a little bit of experience) "know" what a fold does, so chances are anyone reading the code can just glance at a function definition involving fold and instantly "know" what the function does. If you write out the recursion by hand a reader will have to inspect the definitions themselves and reason about the recursive step before they can figure out what it does. Commented Feb 6, 2019 at 8:54
  • 3
    @AJFarmar - product should have a starting value of 1 not 0. (I know you know this and it was a typo or copy/paste error, but best to make sure it's right so the OP isn't confused.) Commented Feb 6, 2019 at 8:58
  • 1
    @RobinZigmond Thank you for this correction, it was intended to be 1. To be clear for the OP: product = foldr (*) 1 is the correct version. Commented Feb 6, 2019 at 10:46

2 Answers 2

2

The idea of folding is a powerful one. The fold functions (foldr and foldl in the Haskell base library) come from a family of functions called Higher-Order Functions (for those who don't know - these are functions which take functions as parameters or return functions as their output).

This allows for greater code clarity as the intention of the program is more clearly expressed. A function written using fold functions strongly indicates that there is an intention to iterate over the list and apply a function repeatedly to obtain an output. Using the standard recursive method is fine for simple programs but when complexity increases it can become difficult to understand quickly what is happening.

Greater code re-use can be achieved with folding due to the nature of passing in a function as the parameter. If a program has some behaviour that is affected by the passing of a Boolean or enumeration value then this behaviour can be abstracted away into a separate function. The separate function can then be used as an argument to fold. This achieves greater flexibility and simplicity (as there are 2 simpler functions versus 1 more complex function).

Higher-Order Functions are also essential for Monads.

Credit to the comments for this question as well for being varied and informative.

Sign up to request clarification or add additional context in comments.

Comments

2

Higher-order functions like foldr, foldl, map, zipWith, &c. capture common patterns of recursion so you can avoid writing manually recursive definitions. This makes your code higher-level and more readable: instead of having to step through the code and infer what a recursive function is doing, the programmer can reason about compositions of higher-level components.

For a somewhat extreme example, consider a manually recursive calculation of standard deviation:

standardDeviation numbers = step1 numbers
  where

    -- Calculate length and sum to obtain mean
    step1 = loop 0 0
      where
        loop count sum (x : xs) = loop (count + 1) (sum + x) xs
        loop count sum [] = step2 sum count numbers

    -- Calculate squared differences with mean
    step2 sum count = loop []
      where
        loop diffs (x : xs) = loop ((x - (sum / count)) ^ 2 : diffs) xs
        loop diffs [] = step3 count diffs

    -- Calculate final total and return square root
    step3 count = loop 0
      where
        loop total (x : xs) = loop (total + x) xs
        loop total [] = sqrt (total / count)

(To be fair, I went a little overboard by also inlining the summation, but this is roughly how it may typically be done in an imperative language—manually looping.)

Now consider a version using a composition of calls to standard functions, some of which are higher-order:

standardDeviation numbers          -- The standard deviation
  = sqrt                           -- is the square root
  . mean                           -- of the mean
  . map (^ 2)                      -- of the squares
  . map (subtract                  -- of the differences
      (mean numbers))              --   with the mean
  $ numbers                        -- of the input numbers
  where                            -- where
    mean xs                        -- the mean
      = sum xs                     -- is the sum
      / fromIntegral (length xs)   -- over the length.

This more declarative code is also, I hope, much more readable—and without the heavy commenting, could be written neatly in two lines. It’s also much more obviously correct than the low-level recursive version.

Furthermore, sum, map, and length can all be implemented in terms of folds, as well as many other standard functions like product, and, or, concat, and so on. Folding is an extremely common operation on not only lists, but all kinds of containers (see the Foldable typeclass), because it captures the pattern of computing something incrementally from all elements of a container.

A final reason to use folds instead of manual recursion is performance: thanks to laziness and optimisations that GHC knows how to perform when you use fold-based functions, the compiler may fuse a series of folds (maps, &c.) together into a single loop at runtime.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.