How to simplify nested map calls?

2019-02-16 15:54发布

问题:

Suppose I have a few nested functors, e.g. List[Option[Int]] and need to call the map of the most inner one.

Now I am using nested maps:

scala> val opts: List[Option[Int]] = List(Some(0), Some(1))
opts: List[Option[Int]] = List(Some(0), Some(1))

scala> opts.map(o => o.map(_ + 1))
res0: List[Option[Int]] = List(Some(1), Some(2))

What if I have 3 nesting levels, for instance ?
Is there any simple alternative to nested maps ?

回答1:

Yes, this is possible with scalaz.Functor:

scala> import scalaz.Functor
import scalaz.Functor

scala> import scalaz.std.list._
import scalaz.std.list._

scala> import scalaz.std.option._
import scalaz.std.option._

scala> Functor[List].compose[Option].map(List(some(0), some(1)))(_ + 1)
res1: List[Option[Int]] = List(Some(1), Some(2))

However, this is longer than to simply call map with a nested map. If you often map nested structures, you can create helper functions:

def map2[F[_], G[_], A, B](fg: F[G[A]])(f: A => B)
  (implicit F0: Functor[F], G0: Functor[G]): F[G[B]] =
  F0.map(fg)(g => G0.map(g)(f))

def map3[F[_], G[_], H[_], A, B](fg: F[G[H[A]]])(f: A => B)
  (implicit F0: Functor[F], G0: Functor[G], H0: Functor[H]): F[G[H[B]]] =
  F0.map(fg)(g => G0.map(g)(h => H0.map(h)(f)))

...

Usage:

scala> map2(List(some(0), some(1)))(_ + 1)
res3: List[Option[Int]] = List(Some(1), Some(2))

scala> map3(List(some(some(0)), some(some(1))))(_ + 1)
res4: List[Option[Option[Int]]] = List(Some(Some(1)), Some(Some(2)))


回答2:

If you have a lot of nested functors and you don't want to flatten them (i.e. they're not monads or you don't want to use them as monads) - then lenses may help. There is quicklens implementation, which supports traversable lenses : http://www.warski.org/blog/2015/03/quicklens-traversing-options-and-lists/.

Example (sorry didn't try to compile it):

modify(opts)(_.each.each).using(_ + 1)

Anyway, you have to specify nesting level, but you don't have to nest functions here. And it's enough to specify it once, like (conceptual example, didn't check):

def md2[T]: (l: List[Option[T]]) => modify(l)(_.each.each)

md2[Int](opts).using(_ + 1)     


回答3:

From the question I understood that you are tying to prune the list iterator e.i. remove upper levels of list in that case you can use flatten which convert list of lists into a single list.

I will be removing few layers of list using flatten

Code:-

val lists = List( 
                    List( 
                        List(
                                List("1"),List("2")
                            ),
                        List(
                                List("3"),List("4") 
                            ) ,
                        List(
                                List("a"),List("b")
                            ),
                        List(
                                List("c"),List("d")
                            ) 
                            )
                  )

val innerVal = lists.flatten.foreach(println)

results :-

List(List(1), List(2))
List(List(3), List(4))
List(List(a), List(b))
List(List(c), List(d))


标签: scala functor