Skip to content

Understand Functions Are Functors

Functions are functors is a basic idea in funcitonal programming. However, the (->) r formats of functions are confusing and misleading, What's worse, affected by imperative language features, there was an misunderstanding about how the original structure is hold by a functor. To be more concrete, this blog talks about the question: why result of fmap (*2) (+100) 1 is 202, not 102?

It's not an introduction of functors itself so I won't be verbose here. However, it's still necessary to know it:

Tip

The Functor typeclass represents the mathematical functor: a mapping between categories in the context of category theory. In practice a functor represents a type that can be mapped over.

The basic concept of functor could make an analogy to imperative language easily, especially with many real examples:

ghci> fmap (*2) [1,2,3]
[2,4,6]
ghci> fmap (*2) $ Just 2
Just 4

Those examples show the idea is simple and easy, something holds a value inside it and a function could be applied on the inside value without affecting the outside structure.

It is definitely true. However, if the mind of the outside structure is just a container, like structures in imperative languages, it greatly thwarts to understand functions correctly. Indeed, this is how I wrongly understand the function as a functor.

Functor and Value are Combined Together

Even though fmap says it could apply the value inside it without affecting outside structure, the functor and inside value are coupled together as one.

Hence, the fmap (*2) (+100) 1 should treated (+100) 1 as one, instead of 1 is hold inside the functor (+100).

Wrong Understanding

If the value inside a functor is treated separatedly from functor, fmap (*2) (+100) 1 will be calculated as listed below:

fmap (*2) (+100) 1
= fmap (*2) ((+100) 1)
= fmap (*2) (f 1) -- f refers to the functor of (+100)
= f (1*2)
= (+100) 2
= 102

This is fully wrong, because it treats them separately. Functor f and value type a is combined together f a, a functor could be represented as ( -> r). Hence, ( -> r) a = ( a->r ), the combination of a function functor is the r finally, which refers to the result after function call instead of the value inside it(which is input).

Correct Understanding

fmap (*2) (+100) 1
= fmap (*2) ((+100) 1)
= fmap (*2) (f 1) -- f refers to the functor of (+100)
= 2 * (f 1)
= 2 * 101
= 202

The confusion could be waived when we use different types in function signatures. If a string append function is applied on the functor which converts from Int to String, it's intuitive that we need to convert the Int to String first, and then concat them together.

Functor of show Function

fmap (++"world") show 2023
= (++"world) "2023"
= "2023world"