what is the map/flatmap function used in for comprehensions?
Asked Answered
N

2

6

I want to see the f function that is getting passed in map/flatmap, but no luck. I threw an an exception to see any sign of f, did not work. What is that function? how is it generated behind the scenes?

Exception in thread "main" java.lang.RuntimeException
   at x.x.Main$A.getInt(Empty.scala:8)
   at x.x.Main$A.flatMap(Empty.scala:10)

object Main extends App {

  class A {
    def getInt: Int = throw new RuntimeException
    def map(f: Int => Boolean): Boolean = f(getInt)
    def flatMap(f: Int => Boolean): Boolean = f(getInt)
  }

  for {
    x <- new A
    y <- new A
  } yield x == y
}
Nieberg answered 15/1, 2014 at 14:46 Comment(1)
It looks like senia actually has a better solution for desugaring things in the repl than I do. Maybe you should accept their answer over mine.Triturable
N
11

The equivalent code for your for-comprehension

for {
  x <- new A
  y <- new A
} yield x == y

is like this:

new A().flatMap{ x => new A().map{ y => x == y } }

You could use scalac -Xprint:parser main.scala to get code generated from your for-comprehension. In this case you'll get this:

new A().flatMap(((x) => new A().map(((y) => x.$eq$eq(y)))))

You could also get it in REPL without addition macro like this:

import reflect.runtime.universe._

show{ reify{
  for {
    x <- new A
    y <- new A
  } yield x == y
}.tree }
// new $read.A().flatMap(((x) => new $read.A().map(((y) => x.$eq$eq(y)))))
Nicks answered 15/1, 2014 at 14:49 Comment(2)
@TravisBrown: thank you, fixed. I should get some rest.Nicks
+1 for the concise, direct answer (as compared to mine) and reference to -Xprint.Triturable
T
11

If you're using Scala 2.10 or higher, you can use the following to show desugared scala code in the repl:

import scala.reflect.macros.Context // use BlackboxContext if you're in 2.11
import scala.reflect.runtime.universe._
import scala.language.experimental.macros

def _desugar(c : Context)(expr : c.Expr[Any]): c.Expr[Unit] = {
  import c.universe._
  println(show(expr.tree))
  reify {}
}

def desugar(expr : Any): Unit = macro _desugar

This will allow you to pass in blocks of code, and see what they translate into. For your example, in the repl:

scala> class A {
     |   def getInt: Int = throw new RuntimeException
     |   def map(f: Int => Boolean): Boolean = f(getInt)
     |   def flatMap(f: Int => Boolean): Boolean = f(getInt)
     | }
defined class A

scala> desugar {
     |   for {
     |     x <- new A
     |     y <- new A
     |   } yield x == y
     | }
new $line15.$read.$iw.$iw.$iw.$iw.A().flatMap(((x: Int) => new $line15.$read.$iw.$iw.$iw.$iw.A().map(((y: Int) => x.==(y)))))

It's a bit messy, because the repl creates several intermediate temporary variables, but you can see the structure of what's happening.

new A().flatMap { (x: Int) =>
  new A().map { (y: Int) =>
    x == y
  }
}

This works for most any expression, and lets you inspect what the actual code will translate to during compilation.

Update

I should point out my source - my version of desugar is a slightly modified version of a function found in the Macrocosm repo on github.

Triturable answered 15/1, 2014 at 14:55 Comment(2)
Even easier: scala> reflect.runtime.universe.reify { for (x <- Nil) yield x } res0: reflect.runtime.universe.Expr[List[Nothing]] = Expr[List[Nothing]](Nil.map(((x) => x))(List.canBuildFrom))Onto
This is an awesome (and wholly unexpected) answer - 10/10Glutton

© 2022 - 2024 — McMap. All rights reserved.