5 ms·
Well, they're a bit weird. I'm not sure why the signature of map magically changes between slide 91 and 92 and find it very confusing. Slide 90 is straightforw
by nrinaudo 12y ago
Well, they're a bit weird. I'm not sure why the signature of map magically changes between slide 91 and 92 and find it very confusing.
Slide 90 is straightforward: Functor is a trait (sort of like an abstract class) that takes a type parameter F, where F is a type constructor that takes one type parameter.
Examples of such type constructors are List (used to create List[Int], List[String], ...), Set (Set[Int]...), Option (Option[String])...
The Functor trait declares a map method that, for any two types A and B, take an instance of F[A], a function from A to B and returns an instance of F[B].
A concrete example: F is List, A is String and B is Int. map takes a list of strings, applies a function that turns strings to ints (say, strlen), and returns a list of ints - the Functor instance for List replaces each of its elements by applying the specified function to it.
A functor in FP really is an endofunctor in the category of types, in which:
- objects are concrete types
- arrows are unary functions
That is, a functor in FP must map:
- concrete types to concrete types
- unary functions to unary functions.
In slide 91, you see that map maps instances of concrete type F[A] to concrete type F[B] - List[String] to List[Int], for example.
In slide 92, you "see" that map maps functions from A to B to functions from F[A] to F[B] - it lifts strlen into a function that takes a List[String] and returns a List[Int].
I'm assuming that slide 92 is meant to show partial application of map, but it really could be made clearer. The author probably explains things more while giving the presentation, but the shift from slide 91 to 92 really is a bit handwavy and magical.
Hope this clarified things a bit, but feel free to ask for more if not, I'd be happy to expand on / correct the parts that you still find obscure.
- im3w1l 12y agoYeah, that helped clarify it alot :) Just one question to check my understanding. That definition of a Functor is not completely general right? Since it always takes an object _ to an object F[_]. Like one functor could be to compose an arrow with addition of 0.5 . And that would map X->int arrows to X->double arrows, right? But that would not be compatibe with the Functor trait?
- nrinaudo 12y agoI'll be using pseudo code and skip over some technical details that are entirely irrelevant to this conversation in my answer. I hope I understood your question correctly and not answering something else entirely. First, a functor doesn't go from A to F[B] but from F[A] to F[B]. A to F[B] looks suspiciously like something a monad does, a topic we don't want to get into. Your example is a functor that maps unary functions to unary functions by composing them. It's a good if slightly complicated example: unary functions have two type parameters (A and B in A => B), while the slide's Functor only takes one. That is, you want to write a Functor[A => B], but you cannot, or at least not directly. What you do is fix one of the parameters. You don't write a Functor for all unary functions, but a Functor for all unary functions whose parameter type is C (for concrete): Functor[C => A], where C is a concrete type and A the functor's type parameter. This functor's map method takes an instance of (C => A), a function f: A => B, and yields a function C => B. The only non-pathological implementation of this is composition: C => A => B What I just wrote makes it sound like you need to write a Functor implementation for all possible parameter types, but that luckily isn't true: writing a function that yields a Functor[C => A] for any given type parameter C is trivial. I'll put the code here just to show it is, but you don't really need to understand it: // I know the C => ? isn't standard Scala, I'm using kind-projector syntax to make this readable. implicit def functionFunctor[C] = new Functor[C => ?] { override def map[A, B](fa: C => A)(f: A => B): C => B = f compose fa } To go back to your concrete example: you just need to get functionFunctor[X] and call map on (x => x * 0.5) to get your function from X to double. Note that some technical details are skipped (partial application, calling the binary function map with a single parameter), but I arbitrarily deem them irrelevant to my point.
- im3w1l 12y agoYou misunderstand my question. I realize in hindsight how it was ambiguous. I used the word "object" like it is used in slide 55. Functors map objects to objects and arrows to arrows. I.e. arrow is the (co)domain of the functor, and object is the (co)domain of the (co)domain of the functor.