Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When map over a function is useful when you have andThen

Tags:

scala

scalaz

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
like image 900
thlim Avatar asked Jul 02 '16 09:07

thlim


1 Answers

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.

like image 54
Travis Brown Avatar answered Sep 30 '22 00:09

Travis Brown