4 ms·
But "apply a function to every object in a list" isn't the definition that the author is getting at! Most programmers probably know "map" as being an operation
by TheAsprngHacker 7y ago
But "apply a function to every object in a list" isn't the definition that the author is getting at!
Most programmers probably know "map" as being an operation takes a function and a list and returns a list of the results of the function applied to each element of the input list. However, the "map" operation can be generalized to generic types other than lists, where it has different behavior but shares certain properties. In functional programming, the idea of a functor gives a name to this pattern.
The idea is that if you have some generic type `T a` covariant in `a`, there is a function `map : (a -> b) -> T a -> T b` that can "lift" every function `f : a -> b` into a function `map f : T a -> T b`. The lifting operation respects function composition and identity.
However, what if the function has multiple arguments, e.g. `f : a -> (b -> c)`? Then, `map f : T a -> T (b -> c)` An applicative functor lets you convert that `T (b -> c)` into a `T b -> T c`. So, with applicative functors, you can individually map each parameter of a multi-parameter function.
A monad is a functor that can be "flattened." It has an operation `join : T (T a) -> T a`, as well as an operation `return : a -> T a`. A monad can be seen as generalizing the idea of flattening a list, or generalizing async/await.
- greydius 7y agoSmall correction: Join should be T (T a) -> T a
- TheAsprngHacker 7y agoOops, thanks.
- anko 7y agoI really like your description. One thing i don't understand though, is why generalising these operations is considered a good thing? In my day to day I spend a bit of time writing functional code, and a lot of time reviewing it, and when you generalise in this manner it hides a lot of the details of the algorithmic complexity. Is this operation happening in a future? or is on a list? You could argue that the type signature will let you know, but quite often it's inferred. Suddenly I find myself needing an IDE just to do code reviews. People make arguments about naming variables and suddenly we're back to using hungarian notation. I also find it's easy to make mistakes - you do a flatmap instead of a map and the Nones just vanish. Or you're composing so many generic functions that the intent of the code just disappears. I guess i just wanted to see if it's just me.
- hopia 7y agoYou can write your own definitions for your types for instances of Functor, Applicative etc. explicitly. Are you worried about the performance when you refer to algorithmic complexity, or what exactly?
- anko 7y agoWell performance is certainly the main factor, but it's really about the Big O's of a function. https://en.wikipedia.org/wiki/Big_O_notation https://en.wikipedia.org/wiki/Big_O_notation
- rovolo 7y ago> is why generalising these operations is considered a good thing? > Is this operation happening in a future? or is on a list? You could argue that the type signature will let you know, but quite often it's inferred. 1) You are generalizing the input of a function to multiple types. Many of the functions you'd write wouldn't care whether you're using a List or a Future, they would accept either. It's just like declaring your Java method to take a Collection instead of an ArrayList. 2) You are getting more specific in the output of the function. If you write `filter`, it would be nice to get a `LinkedList` back if you pass in a `LinkedList`. With more common type systems, the best you'll be able to do writing the function once is a return type of `Collection` or `Iterable`. Your method may perform horribly if a LinkedList is passed in instead of an ArrayList. If your operation really depends on a specific behavior the Functor doesn't specify, you should be using a different type than Functor.
- sweeneyrod 7y ago> Many of the functions you'd write wouldn't care whether you're using a List or a Future, they would accept either. I can't imagine a situation where that would be true, except if you're writing a monad library. You could say that you might want to interchange a list of futures for a future of lists, but that's two interdependent things being swapped not one.
- 7y ago