Functors in Haskell
In this post I'll write about functors only in programming terms, specifically in Haskell.
In the last post we discussed the functor. A functor is not itself a type, it's a type constructor as given by the definition of the typeclass in Haskell:
The typeclass requires an implemention of for every instance of that typeclass. is a higher order function which takes a function and "lifts" it into a function that acts on the function which itself is a mapping from functor-to-functor.
This means really that we can take any ordinary function and apply it to a functor, which then will return a functor.
List is a functor
A list in Haskell is a coproduct type defined recursively as:
The built-in list type has a functor instance. Here's the implementation:
We simply apply the function to the head of the list, then that function over the tail of the list and combine them.
The Reader functor
This is something that is, on the surface, quite different to the functor examples shown before ( and ).
In Haskell functions are types. A partially applied function is also a type in Haskell - here we take a function and partially apply it - it's a function that's awaiting a second type argument , this is also a type constructor. When it receives it's second type it becomes a full type - a total function.
This type constructor (partially applied function) defines a whole family of type constructors parameterized by the type . Here, in the context of the functor we want to take a type and map it into a type . In order to show that is a functor we want to lift the original function into . In Haskell:
That is, we apply and then - it's the only way to make the two arguments typecheck. In pointfree form:
In simple terms it's a thing that takes a resource , () to which we apply a regular function . Here's an example:
In other words we have as a partially applied function which is awaiting it's argument - we have a resource to which we pass as the first argument to the partially applied function , the result of this gets passed to which is - so the result is 303 given as above. Remember we are dealing with partially applied functions, so the whole process kicks off when we are given an . This is applied to the second function and the result of that is passed to the first function.
There is a lot more to the functor - it's actually a which itself "derives" from . This is just some of the detail in the context of functors.
Functor composition
This follows from the rules of category. Given the following function:
We have a function that takes a functor to another functor which is itself a functor containing a functor. In order to apply some function to this we need to into the outer functor, and then again to break into the inner functor. A simple example on a list of integers that may or may not have a tail:
Gaining intution
Functors can be thought of as a context in which some value(s) is/are stored. For example the functor is a context that may or may not contain a value. is a context that contains those values that come from I/O and are thus unpredictable, and so on.
This doesn't always make the most sense as functors like the list functor are better thought of as a container. There are other functors like the constant functor that don't map well to this context worldview, and so are better thought of as a thing that contains something, but we don't really care about what it contains. We only care about preservation of the structure of the categories upon which the functors act on.
In a nutshell the functor typeclass allows us to apply pure functions to values that are in a context/container - this means we are separate our program logic neatly into pure and effectful parts.