5

Why does the type class Functor have only the memberfmap? I often find it useful to perform a fold over datatypes. Say, hierarchical ones, like a tree.

Note that a map is a special case of fold, that is, the latter is more fundamental. I guess I can take it the way it is, but maybe there is a reason?

akonsu
  • 28,824
  • 33
  • 119
  • 194
  • 5
    How would you define `fold` on `IO`, which is a `Functor`, but not `Foldable`? – Zeta Jul 30 '15 at 13:33
  • 2
    How is `fmap` a special case of `fold`? I can't imagine how you would be able to implement either in terms of the other, which is what I think of when I think of "more fundamental". – Dietrich Epp Jul 30 '15 at 13:47
  • the `fmap` in my post was a typo. I mean `map`. – akonsu Jul 30 '15 at 14:01

2 Answers2

14

Because a Functor is a very general kind of object; not all Functors support folds. For example, there is an instance1

instance Functor (a ->) where
     -- > fmap :: (b -> c) -> (a -> b) -> (a -> c)
     fmap f g = g . f

But, while (a ->) is a Functor for all a, for infinite a there isn't a reasonable fold definition. (Incidentally, a 'fold' in general is a catamorphism, which means it has a different type for each functor. The Foldable type class defines it for sequence-like types.).

Consider what the foldr definition for Integer -> Integer would look like; what would the outermost application be? What would the value of

foldr (\ _ n -> 1 + n) 0 (\ n -> n + 1)

be? There isn't a reasonable definition of fold without a lot more structure on the argument type.

1 (a ->) isn't legal Haskell for some reason. But I'm going to use it anyway as a more readable version of (->) a, since I think it's easier for a novice to understand.

amalloy
  • 89,153
  • 8
  • 140
  • 205
Jonathan Cast
  • 4,569
  • 19
  • 34
  • 3
    I think I see where I was wrong: `map` is a special case of `fold` only for lists (maybe for something else) but not in general. Thanks. – akonsu Jul 30 '15 at 14:39
  • 1
    Yeah, `fold` is way more powerful on lists than on other types (especially something like arbitrary trees). Formally (ignoring laziness), the list functor is the free monoid functor, which is the left adjoint of the forgetful functor from monoids to sets; the general `fold` function is then one direction of the hom-set adjunction between the list functor and the forgetful functor. As such, every monoid homomorphism on lists (which includes `map`) is given by some instance of `fold`. – Jonathan Cast Nov 13 '17 at 16:29
8

The abstraction you are looking for is Foldable: This is a type class providing variuos forms of fold.

There is also Traversable for things where you can “fold while changing”, comparable to the mapAccumL function for lists.

It is actually not true that fmap is a special case of fold, as pointed out by @DietrichEpp, see as here are possible non-Functor instances of Foldable.

map is however a special case of mapAccumL (no surprise, given the name), and hence the definition of Traversable requires the thing to be also a Functor and a Foldable.

Community
  • 1
  • 1
Joachim Breitner
  • 25,395
  • 6
  • 78
  • 139
  • thanks. this is interesting. I am a beginner, I vaguely understand what you are saying. in general, though, it seems confusing that a `Functor` is not necessarily a `Foldable`. Given that `map` is defined in terms of `fold`... – akonsu Jul 30 '15 at 13:37
  • 2
    That's exactly the reason, though: if *`fold` were defined in terms of `map`*, then we could define `fold` for every `Functor`, since we could use the existing `map` to get `fold` for free. – Lynn Jul 30 '15 at 13:40
  • @DietrichEpp's hinting the right thing; there are [possible non-Functor instances of Foldable](http://stackoverflow.com/questions/8359115/haskell-an-example-of-a-foldable-which-is-not-a-functor-or-not-traversable). – Lynn Jul 30 '15 at 13:47
  • Sorry, confused `fold` with `traverse`; fixed the answer accordingly. – Joachim Breitner Jul 30 '15 at 13:52