A fold is a function that takes a piece of data in a structure, and collapses it to another piece of data. Usually we do this to "reduce" a collection to a single value. That's why if you look at other languages like Lisp, Smalltalk, Ruby, JavaScript, etc you'll find this operation called reduce
, which is the poor cousin of folding in Haskell.
I say it's the poor cousin because your intuition on lists is correct, but in Haskell we're much more abstract and general, so our fold functions can work on any kind of structure whose type we've told haskell what fold means for.
So, we can talk about "using addition with fold to turn a list of numbers into a sum value", or we can talk about "using a function to take a family tree of names and collapse it to a list", and so on and so forth. Any time we have this idea of changing the structure of something to a single value, or maybe to a different structured set of values, that's folding.
The "canonical" way to represent this in Haskell is foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
but it's easier like you've thought to use "a list of a
" as the Foldable f => t a
type at the beginning coz it's a bit easier to understand. So we have a specialised type of foldr :: (a -> b -> b) -> b -> [a] -> b
. But what are a
and b
? and what is (a -> b -> b)
and what do these three arguments do?
Let's specialise it to Int
values for both a
and b
: foldr :: (Int -> Int -> Int) -> Int -> [Int] -> Int
wow... that makes it ... interesting doesn't it? so foldr
takes a function of two ints, like, say, the (+)
function, and it takes a single Int
value (this is the initial value it will use as the target result, and a list of Int
values... then it'll produce a single Int
from that... that is, it takes that Int -> Int -> Int
function and applies it to the single Int
and the first of the [Int]
, then applies that function to that result and the next value of the [Int]
and so on and so forth until there are no more [Int]
left... then that's what it returns.
It's quite literally folding the function over the data structure.
That's all well and good for lists, which are a single straight line, but what you have is a tree, not a list. So how does it work there? Well, let's see how we might specialise foldr
to produce a pair of the highest and lowest numbers from a list of Int
instead? foldr :: (Int -> (Int, Int) -> (Int, Int)) -> (Int, Int) -> [Int] -> (Int, Int)
. So we take a function that takes an Int
and a pair, and we put the initial pair into it, along with the first Int
from our [Int]
. That returns us a new pair, and we then do the same process for the next of the [Int]
and then we continue that process untill all we're left with is a pair at the end.
foldToMinMax = foldr (\newNum (minnum,maxnum) -> (min minnum newNum, max maxnum newNum)) (maxBound :: Int, minBound :: Int)
So now things are getting a little bit clearer.
What about this tree of flowers you have, though? Well, you need to write yourself a folding function that will take two values, one of which will be the same value as the initial value and the result value, and the other of which will be the type of your tree, and build a value of the result type. If I were to use pseudo code to write the types in a more descriptive way, I'd probably write something like: foldr :: (contentOfCollectionType -> resultType -> resultType) -> resultType -> (collectionWrapper contentOfCollectionType) -> resultType
But you don't have to use foldr
here, in fact you can't use it unless you do some fancy typeclass instantiation stuff anyway. You can totally write your own folding function using plain recursion. That's what they're after.
If you want to learn about recursion and folding and whatnot, and you don't understand these things yet, I recommend the book I helped author. http://happylearnhaskelltutorial.com It explains it in much more detail and with many clear examples. If you understand the basics, it should be pretty quick to get up to speed on the point where you want to know about recursion and folding... but if you don't, well it's going to be very useful for you to understand the basics because you need to know them before getting to the other stuff.
I should mention that your particular fold also has a conversion function in it. It's the thing that converts a Color
to an x
. The function that you were given to work with as a folding function "crushes x's together" (ie takes two x
values and produces another x
value, very similar to (+)
in our examples above). It can only work on trees because we also give it this function to turn a Color
into an x
, which effectively takes the meaningful data out of the tree and puts it into a form that the folding function can use.
There's a very beautiful pattern at work here.
Good luck!
fold
would work left to right? – Autonomousleaf blossom stalk
so to make it obvious what they replace. Add some recursion. – WarmheartedFoldable
. It's a plain catamorphism. – Warmhearted