Having read the book Learn you a Haskell For Great Good, and the very helpful wiki book article Haskell Category Theory which helped me overcome the common category mistake of confusing category objects with the programming objects, I still have the following question:
Why must fmap map over every elements of a List?
I like that it does, I just want to understand how this is justified category theoretically. ( or perhaps it is easier to justify using HoTT? )
In Scala notation, List is a functor that takes any type and maps that into an type from the set of all list types, eg it maps the type Int to the type List[Int] and it maps the functions on Int eg
Int.successor: Int => InttoFunctor[List].fmap(successor) : List[Int] => List[Int]Int.toString: Int => StringtoFunctor[List].fmap(toString): List[Int] => List[String]
Now every instance of List[X] is a monoid with a empty function (mempty in Haskell) and combine function (mappend in Haskell). My guess is that one can use the fact that Lists are Monoids, to show that map has to map all elements of a list. My feeling here is that if one adds the pure function from Applicative, this gives us a list with just one element of some other type. e.g Applicative[List[Int]].pure(1) == List(1). Since map(succ) on those elements gives us the singleton list with the next element, this covers all those subsets. Then I suppose the combine function on all those singletons gives us all the other elements of the lists. Somehow I suppose that constrains the way map has to work.
Another suggestive argument is that map has to map functions between lists. Since every element in a List[Int] is of type Int, and if one maps to List[String] one has to map every element of it, or one would not the right type.
So both of those arguments seem to point in the right direction. But I was wondering what was needed to get the rest of the way.
Counterexample?
Why is this not a counterexample map function?
def map[X,Y](f: X=>Y)(l: List[X]): List[Y] = l match {
case Nil => Nil
case head::tail=> List(f(head))
}
It seems to follow the rules
val l1 = List(3,2,1)
val l2 = List(2,10,100)
val plus2 = (x: Int) => x+ 2
val plus5 = (x: Int) => x+5
map(plus2)(List()) == List()
map(plus2)(l1) == List(5)
map(plus5)(l1) == List(8)
map(plus2 compose plus5)(l1) == List(10)
(map(plus2)_ compose map(plus5)_)(l1) == List(10)
Ahh. But it does not fit the id law.
def id[X](x: X): X = x
map(id[Int] _)(l1) == List(3)
id(l1) == List(3,2,1)