I\'m trying to learn Haskell and I\'m through all the basics. But now I\'m stuck, trying to get my head around functors.
I\'ve read that \"A functor transforms one
It's important to keep separate in your head the distinction between a functor itself, and a value in a type which has a functor applied to it. A functor itself is a type constructor like Maybe, IO, or the list constructor []. A value in a functor is some particular value in a type with that type constructor applied. e.g. Just 3 is one particular value in the type Maybe Int (that type is the Maybe functor applied to the type Int), putStrLn "Hello World" is one particular value in the type IO (), and [2, 4, 8, 16, 32] is one particular value in the type [Int].
I like to think of a value in a type with a functor applied as being "the same" as a value in the base type, but with some extra "context". People often use a container analogy for a functor, which works pretty naturally for quite a few functors but then becomes more of a hindrance than a help when you're having to convince yourself that IO or (->) r is like a container.
So if an Int represents an integer value, then a Maybe Int represents an integer value that may not be present ("may not be present" is the "context"). An [Int] represents an integer value with a number of possible values (this is the same interpretation of the list functor as the "nondeterminism" interpretation of the list monad). An IO Int represents an integer value whose precise value depends on the entire universe (or alternatively, it represents an integer value that can be obtained by running an external process). A Char -> Int is an integer value for any Char value ("function taking r as an argument" is a functor for any type r; with r as Char (->) Char is the type constructor which is a functor, which applied to Int becomes (->) Char Int or Char -> Int in infix notation).
The only thing you can do with a general functor is fmap, with the type Functor f => (a -> b) -> (f a -> f b). fmap transforms a function that operates on normal values into a function that operates on values with additional context added by a functor; what exactly this does is different for each functor, but you can do it with all of them.
So with the Maybe functor fmap (+1) is the function that computes a possibly-not-present integer 1 higher than its input possibly-not-present integer. With the list functor fmap (+1) is the function that computes a nondeterministic integer 1 higher than its input nondeterministic integer. With the IO functor, fmap (+1) is the function that computes an integer 1 higher than its input integer-whose-value-depends-on-the-external-universe. With the (->) Char functor, fmap (+1) is the function that adds 1 to an integer which depends on a Char (when I feed a Char to the return value, I get 1 higher than what I would have got by feeding the same Char to the original value).
But in general, for some unknown functor f, fmap (+1) applied to some value in f Int is "functor version" of the function (+1) on ordinary Ints. It adds 1 to the integer in whatever kind of "context" this particular functor has.
On its own, fmap isn't necessarily that useful. Usually when you're writing a concrete program and working with a functor, you're working with one particular functor, and you often think of fmap as whatever it does for that particular functor. When I'm working with [Int], I'm often not thinking of my [Int] values as nondeterministic integers, I just think of them as lists of integers, and I think of fmap the same way I think of map.
So why bother with functors? Why not just have map for lists, applyToMaybe for Maybes, and applyToIO for IOs? Then everyone would know what they do and no one would have to understand weird abstract concepts like functors.
The key is the recognition that there are a lot of functors out there; almost all container types for a start (hence the container analogy for what functors are). Every one of them has an operation corresponding to fmap, even if we don't have functors. Whenever you write an algorithm solely in terms of the fmap operation (or map, or whatever it's called for your particular type), then if you write it in terms of functors rather than your particular type then it works for all functors.
It can also serve as a form of documentation. If I hand off one of my list values to a function you wrote that operates on lists, it could do any number of things. But if I hand off my list to a function you wrote that operates on values in an arbitrary functor, then I know that the implementation of your function can't be using list features, only functor features.
Thinking back to how you would use functorish things in traditional imperative programming might help see the benefits. There container types like arrays, lists, trees, etc, will usually have some pattern you use to iterate over them. It can be slightly different for different containers, although libraries often provide standard iteration interfaces to address this. But you still end up writing a little for-loop every time you want to iterate over them, and when what you want to do is calculate a result for each item in the container and collect all the results you typically end up mixing in the logic for constructing the new container as you go.
fmap is every for loop of that form you will ever write, sorted once and for all by the library writers, before you even sit down to program. Plus it can also be used with things like Maybe and (->) r that probably wouldn't be seen as having anything to do with a designing a consistent container interface in imperative languages.