Reputation: 634
Could you please explain why the second call to listen method does not work and suggest a solution to make it work?
object Example extends App {
case class Event(kind: Int)
trait Listener { def handle(e: Event) }
val listeners = scala.collection.mutable.Map[Int, Listener]()
def emit(e: Event) = listeners.get(e.kind).foreach(_.handle(e))
def listen(kind: Int)(f: (Event) => Unit) {
val l = new Listener() { override def handle(e: Event) = f(e) }
listeners += kind -> l
}
implicit def unit2EventUnit(f: => Unit) = (e: Event) => f
// This works as expected
println("1")
listen(1) { e: Event => println("a"); println("b") }
println("2")
emit(new Event(1))
println("3")
// Why this does not work? "c" should appear between "4" and "d", not "3" and "4"
listen(2) { println("c"); println("d") }
println("4")
emit(new Event(2))
println("5")
}
I know this would work:
implicit def unit2EventUnit2(f: () => Unit) = (e: Event) => f()
listen(2) { () => println("c"); println("d") }
But that would be simpler to write:
listen(2) { println("c"); println("d") }
Upvotes: 0
Views: 127
Reputation: 24403
The compiler only sees a block in listen(2) { () => println("c"); println("d") }
. This block however does not match the signature of the listen function, but it returns a Unit
and there is an implicit conversion from Unit
to Event => Unit
, so it takes the last expression in the block and applies the conversion. That's why you get this result. I don't see an obvious solution to this, but you could just ignore the parameter in your function and write it as
listen(2) { _ => println("c"); println("d") }
Then you also don't need the implicit conversion. This kind of conversions shouldn't be used anyway, because they make your code hard to reason about for people who don't know of the conversion and also it is a possible source for unexpected behavior.
Upvotes: 3