Runar recently made mention of these so-called Functors and Monads in his excellent post about concurrency/actors in Java. There are all sorts of tutorials out there for understanding what a Monad is, however, I am of the opinion that one must first understand what a Functor is. This is because it is less complex and more general (all monads are functors plus more).
The Java Posse also made a couple of false claims about Runar’s article, but one in particular needs addressing. This is the notion that a functor is applicable to “Functional Programming”. This is false. A functor is applicable to programming. You really, really need to understand what a functor is, if you’re ever going to be skilled in any type of programming; even if it’s just Java web applications for the rest of your life. This concept does not exist ‘on the other side of the fence’ in the imaginary ‘functional programming world’. These suggestions are understandable of course and I don’t intend to labour the point — only make this point clear, since these apparent divides seem to pop up often. On with the story…
In Java, we have an interface called CharSequence. A few things can be said about CharSequence. How about:
All implementations guarantee that for some integer
n, then ifn >= 0 && n < length(), thencharAt(n)will not throw an exception.
We might call this a law about CharSequence that all implementations must satisfy. We could even find other laws that must satisfy about implementations of the CharSequence interface.
A Functor is an interface and it has two laws. Sadly though, this interface cannot be expressed using Java’s type system — it is simply not clever enough. We must use a hypothetical Java to express it. First though, we have to make up for Java’s missing first-class functions by using an interface. This is easy:
interface Function { B f(A a); }
OK, so this a just an interface like any others. It needn’t satisfy any laws; it just takes an argument (A) and returns (B). We could write bazillions of implementations of this interface in Java. We’re going to need this interface to write the Functor interface.
Now, the missing part of Java is called higher-kinds — a term you needn’t concern yourself with too much. I hope this hypothetical syntax is enough to make the point clear:
interface Functor<e> {
E fmap(Function f, E fa);
}
So the foreign part here is E. Consider E to stand for ‘Environment’. It is a type parameter that takes yet another type parameter (which is why it is denoted E). This means I could use List or Set, but not Integer as the type parameter value for E, since List and Set take another type parameter while Integer does not. You might think of the Functor interface as ‘applying the given function within the environment’.
We might write an implementation like this:
class ListFunctor implements Functor {
public List fmap(Function f, List list) {
// todo: apply the given function on all elements in 'list' and return the result
}
}
In general discussion, we would call this the list functor, simply because the value for the environment has been applied. In Runar’s article, he was talking about the Callable functor; yet another environment. There are many possibilities for the environment here; List and Callable are but two.
The Laws
Let us not forget the laws
Just like all implementations of CharSequence have laws to obey, so do all functors. They are called identity and composition.
Identity
Remember the Function interface? Here is an implementation:
class Identity implements Function { public A f(A a) { return a; } }
If we are given an implementation of Functor — which I will call f — then it must satisfy f.fmap(new Identity(), x) is equivalent to x (for any value of x). So that’s the identity law out of the way. Mapping the identity function across a functor yields the same value.
Composition
The law of composition is a little less friendly when it comes to expressing it in Java so I will refrain
While it is an important part of making up the concept of a functor, it may be best to defer this final piece of the puzzle for now.
For completeness, I will state the law using a shorter syntax, but if this is foreign or worrying, then please ignore it: f.fmap(a compose b, x) is equivalent to f.fmap(a, f.fmap(b, x)) (for any value of a, b and x — a and b are Function objects).
That’s all there is to a Functor. If you use the map function on lists or perhaps the map method on scala.Option, then you are using one specific instance of a functor, since these functions/methods satisfy the conditions for a functor. Even throwing an exception is using a specific functor! Perhaps that can be explained another time
I should also add that when referring to a functor (unqualified), we are referring to a covariant functor. Other types of functors include contra-variant and invariant functors. We can talk about those another day
Next: Just What the Flip is a Monad?
Questions?
Could you a very basic article on functors and monads. I didn’t understand a word from your explanation. Sorry
Sergej, this article is pretty good if you don’t mind it being from a Haskell point of view:
http://en.wikibooks.org/wiki/Haskell/Category_theory
despite the scary name it is a very clear explanation of functors and monads, and the mathematical structures (called categories) that type systems (among other things) live inside of.
It sort of bothers me that most explanations of functors and monads focus on the laws in an abstract sense, rather than explaining what the heck they’re good for. Don’t get me wrong, I understand why the laws are important, but stripping away the academics, most developers are content to simply take such laws as intuitive specifications and move on from there.
Anyway, as I understand it…
Functors are used to map higher-order functions over each element in a container. This is useful in the abstract sense because it allows you to apply an operation to each element of a container and then do something useful with the result. In a pure-functional language, this sort of operation is absolutely essential in order to do anything useful. Anyway, an example of the map concept drawn from Scala:
val strNums = List(“1″, “2″, “3″, “4″, “5″)
val nums = strNums.map((num) => num.toInt)
nums == List(1, 2, 3, 4, 5) // true
I could have written the above a bit more concisely, but I thought the more verbose syntax would help keep things clear. Map literally means that for each element in the List, apply the function and store the result in the corresponding location in a new List. This new list is then returned as a result from the map function.
The above is sort-of a trivial example of why this sort of thing is useful. There are many times when I’ve had to do things like this in the real world, but all such examples are too large to fit into this comment.
As I said, the laws are pretty intuitive, so for the most part we can ignore them. Quickly expressed in Scala, however:
// identity
val nums = strNums.map((num) => num)
nums == strNums // true
// composition
def convertToInt(a: String) = a.toInt
def addTwo(a: Int) = a + 2
val nums1 = strNums.map((num) => addTwo(convertToInt(num)))
val numsTemp = strNums.map((num) => convertToInt(num))
val nums2 = numsTemp.map((num) => addTwo(num))
nums1 == nums2 // true
Basically, the composition law says that mapping the composition of two functions (in this case, addTwo and convertToInt) is equivalent to first mapping the one, then mapping over the result of the first with the second. As I said, quite intuitive.
Anyway, what I’ve shown isn’t *precisely* functors, since the map function is integrated into List, rather than part of a separate container. Hopefully it helps clarify things though. At least, I think that’s right. As I said, I’m still a bit foggy on all this myself.
To punctuate all of the above, it’s worth remember that Tony’s really the expert here on this sort of stuff, I’m still trying to figure it all out. My intention was not to supercede his post, just to try to clarify some of the motivation behind it. Take all of what I say with a heavy grain of salt.
I’ll take the opposite tack from Daniel Spiewak (at least initially) and step back to the underlying mathematics. Say you have two different kinds of things, in Daniel’s example they were objects (we’ll call them X) and lists of objects (we’ll call them Y). So when we say X we mean all objects and all functions/methods on them. When we say Y we mean all lists of objects and all functions/methods on them. For a mapping from X to Y to be a Functor it has to do two really sensible things. They are so sensible that mappings that don’t do these things are just much less useful.
Let’s consider a functor named F. It takes objects and maps them to list of objects. It also (and this is crucial) takes functions from objects to objects and maps them to functions from lists of objects to other lists of objects. But how do we talk about mapping functions? That’s not immediately obvious, but really it just means that if we have a function in X:
f: x1 in X to x2 in X
then after we apply the function F to it we have the function g in Y that behaves as follows, if f(x1) = x2 and F(x1) = y1, and F(x2) = y2 then g is the function that is guaranteed to give you g(y1) = y2. So g has to “do the same thing” as f. That’s why in Daniel’s example the function that adds 2 to an integer correspond to the function that adds 2 to each integer in a list.
1) There is an identity function on X that takes an object and returns the same object. There is also an identity function on Y that takes a list of objects and returns the same list of objects. So let’s look at how this works on some example numbers like 1, 2 & 3.
We’ll call the identity function in x idx() and we’ll call the identity function in y idy(), we want F(idx()) = idy(). So we want:
List(idx(1), idx(2), idx(3)) = idy(List(1,2,3))
Since idx(1) = 1, idx(2) = 2, and idx(3) = 3, that means we want
List(1,2,3) = idy(List(1,2,3))
But that’s exacly what idy() does, it takes a list and returns the same list. So that’s the first thing that a functor has to do, it has to “preserve” the identity function. What’s the 2nd?
2) A functor has to “preserve” composition. So if we have two different functions in X
f(x) = x+2
g(x) = 3*x
Then we can create a new function g(f(x)) = 3*(x+2). Now how does F map these functions from X to Y? The following are all functions in Y (from lists to lists)
F(f())
F(g())
F(g(f())
That’s a lot of parenthese, let’s give them names:
p() = F(f())
q() = F(g())
r() = F(g(f())
The composition requirement is that:
r() = q(p())
So for our two functions
f(x) = x+2
g(x) = 3*x
We require that
List(g(f(1)), g(f(2)), g(f(3)) = q(p(List(1,2,3))
Let’s start by looking at the left side
List(g(f(1)), g(f(2)), g(f(3))
= List(g(3),g(4),g(5))
= List(9, 12, 15)
But how do we evaluate the right side?
q(p(List(1,2,3))
Well, we know that p() = F(f()) is the function that maps a list to the same list with f() applied to each of it’s elements. So,
q(p(List(1,2,3))
= q(List(f(1), f(2), f(3))
= q(List(3,4,5))
= List(g(3), g(4), g(5))
= List(9, 12, 15)
So Daniel’s mapping is a Functor since it obey’s the identity requirements and it obeys the composition requirement.
So what that some (or in this case all) of the fundamental behaviors of X are seen in Y. Daniel’s Functor is the “natural” functor from objects to lists of objects. It does the simplest thing possible without throwing away anything. But there are other functors that only retain some of the information/behavior when they map from some X to some Y. They let us look or capture a portion of the complexity of X in a more studiable Y. Or maybe they just handle a bit of i/o as in Haskell.
We require that
While reading the posting I was trying to come up with a more informal definition of a functor, which I (as a newbie) could understand more easily. I was thinking of something like “a construct to which you provide a function and an environment (or container) in order to transform all elements contained.” Then I saw Daniel Spiewak’s comment. I think it complements the post nicely. I’m glad to be exposed to some of the formal stuff, but I’d be especially grateful to see also an appeal to a more natural, intuitive view of things, if you know what I mean.
@Daniel S, @Scott, and, of course, @Tony: in order to get a better handle on the mathematics you’re talking about I’m (re)taking the college calculus slate (gotta start somewhere
. First thing, in calc 1, we talked about domains and ranges of functions.
When I read about functors, I see a new term: codomain. How is this different from the range of a function? In Scott’s post, F has type X->Y; is Y the range? How about the original function; in the context of category theory, is it valid to say that f has type X->X and therefore (one subset of) X is the domain and (another, possibly different, subset of) X is the range?
Daniel,
Yes, codomain is just a more formal term for range. It is common in category theory to name something and then give if it has a “dual” thing to name that co-whatever. By “dual” we just mean the thing that we would get if we reversed the direction of the functions (“morphisms”). So if f: X -> Y then X is the domain and since the reverse morphism is cof: Y -> X we call Y the codomain.
It so happens that if a given category is interesting then its dual is almost always interesting too. Hence all of this “co-whatever” terminology.
Re: codomain vs range, my understanding of the difference between the two is that the codomain is the type of the result, whereas the range is the set of instances (within the codomain) that can actually be returned.
For example, a function that takes an integer and doubles it has a codomain of integer, however, the range of the function is the set of even integers.
I am not an expert in this area, so perhaps someone else can verify this?
Thanks Scott for your comments. They have been very helpful for others.
Yes, I forgot to mention that I’m one of those who found Scott’s comment very helpful! Combined with Tony’s post and the link to the Haskell Category Theory page on wikibooks, I think it’s all falling into place.
[...] by Daniel’s comment on Tony’s blog, I figured I’d start documenting the times I use [...]
[...] and monads on the Internet, and I’ve written about them before, with examples in Java. Tony Morris also wrote a great post where he explains functors as “function application in an environment”. That’s a [...]