With Scalaz, a function can be mapped over another function. When would I want to use map
over andThen
? Is there a clear advantage using map
? Thanks
For example,
val f: Int => Int = (a) => a + 10
val g: Int => Int = (a) => a * 100
(f map g map {_*3})(10) == (f andThen g andThen {_*3})(10) // true
Setting aside implementation details for a moment, map
is andThen
for functions (under the functor instance for A => ?
), and it doesn't really make a lot of sense to talk about preferring one over the other if we're talking about functions specifically and not some higher level of abstraction.
What methods like map
(and type classes like Functor
more generally) allow us to do is abstract over specific types or type constructors. Suppose we want to write a incrementResult
method that works on both A => Int
or Kleisli[Option, A, Int]
, for example. These types don't have anything in common in terms of inheritance (short of AnyRef
, which is useless), but A => ?
and Kleisli[Option, A, ?]
are both functors, so we could write this:
import scalaz._, Scalaz._
def incrementResult[F[_]: Functor](f: F[Int]): F[Int] = f.map(_ + 1)
And then use it like this (note that I'm using kind-projector to simplify the type syntax a bit):
scala> val plainOldFuncTriple: Int => Int = _ * 3
plainOldFuncTriple: Int => Int = <function1>
scala> val optionKleisliTriple: Kleisli[Option, Int, Int] = Kleisli(i => Some(i * 3))
optionKleisliTriple: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>)
scala> val f = incrementResult[Int => ?](plainOldFuncTriple)
f: Int => Int = <function1>
scala> val k = incrementResult[Kleisli[Option, Int, ?]](optionKleisliTriple)
k: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>)
scala> f(10)
res0: Int = 31
scala> k(10)
res1: Option[Int] = Some(31)
In this case specifically there are better ways to implement this operation, but it shows the general idea—we couldn't write a single method that works for both ordinary functions and Kleisli arrows using andThen
, but we can with the extra level of abstraction that map
gives us.
So to answer your question—you'd use map
if you want to abstract over all type constructors that have a functor instance, but if you're working specifically with functions, map
is andThen
, and—as long as we're still setting aside implementation details—it doesn't matter which you choose.
Footnote: the map
that Scalaz's syntax
package gives you for values of types that have functor instances is implemented as an extension method, so there's a tiny bit of overhead (both at compile time and runtime) involved in using map
instead of andThen
on a function. If you're only working with functions and don't need the extra abstraction, then, you might as well go with andThen
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With