Block implicit conversion location selection

In the following code, implicit conversion is applied around the println(2) ; I foolishly expected it to apply to the whole block { println(1); println(2) } { println(1); println(2) } . How can I explain where the compiler places the implicit?

 object Executor { private var runnable: Runnable = _ def setRunnable(runnable: Runnable) { this.runnable = runnable } def execute() { runnable.run() } } object Run extends App { implicit def blockToRunnable(p: โ‡’ Any): Runnable = new Runnable { def run() = p } Executor.setRunnable { println(1) println(2) } println("Before execute") Executor.execute() } 
+4
source share
4 answers

According to the specification, an implicit conversion is applied when the type of an expression does not match the expected type . The main observation is how the expected type is sliced โ€‹โ€‹when entering blocks.

if the expression e is of type T and T does not match the expressions of the expected type pt . In this case, an implicit v is searched, which is applicable to e and the result of which matches pt.

In Section 6.11 โ€œBlocks,โ€ the expected type of the last block expression is defined as

The expected type of the final expression e is the expected type of the block.

Given this specification, it seems that the compiler should behave this way. The expected block type is Runnable , and the expected type println(2) also becomes Runnable .

Suggestion: if you want to know which implications apply, you can use the nightly build for 2.1 from the Scala IDE for Eclipse. He can "highlight the implications."

Edited by:. I admit that this is surprising when a call by name is implicit in scope.

+3
source

I rationalize this behavior as follows: according to the specification, the block type {s1; s2; ...; sn; e } {s1; s2; ...; sn; e } {s1; s2; ...; sn; e } is the type of the last expression e .

So, the compiler accepts e , and the type checks it for Runnable . This fails, so it looks for an implicit conversion that converts e to Runnable . Therefore, he would like to do this:

 { s1; s2; ... sn; convert(e) } 

This is confirmed by scala -Xprint:typer with this small example:

 class A implicit def convert(a: A): String = a.toString def f(s: String) { println(s) } f{ println(1); new A } 

prints:

 private[this] val res0: Unit = $line3.$read.$iw.$iw.f({ scala.this.Predef.println(1); $line2.$read.$iw.$iw.convert(new $line1.$read.$iw.$iw.A()) }); 
+4
source

The problem is that you think of blocks as if they were thunks, as if they were code snippets. This is not true. { a; b; c } { a; b; c } not part of the code that can be passed.

So, how should you reason about implications? Actually, how should you reason about representations that are implicit transformations. Views apply to the value that needs to be changed. In your example, the value

 { println(1) println(2) } 

passed to setRunnable . The value of a block is the value of its last expression, so it passes the result from println(2) to setRunnable . Since this Unit and setRunnable requires a Runnable , an implicit search is found, so println(2) is passed to the grossly incorrect name blockToRunnable .

The bottom line is there, and this is the advice that I have given many times on Qaru (many people try to do the same) to get the following in your head:

 THERE ARE NO BLOCKS IN SCALA. 

There are functions, but not blocks.

Technically, this statement is wrong - there are blocks in Scala, but they are not what you think of them, so just completely remove them from your mind. You can find out which blocks in Scala are the last, from scratch. Otherwise, you should try to get them to work in ways they donโ€™t do, or to conclude that everything works in a certain way when they work differently.

0
source

I really liked the explanation given in the scala puzzle .

In other words, what will be the result:

 List(1, 2).map { i => println("Hi"); i + 1 } List(1, 2).map { println("Hi"); _ + 1 } 
0
source

Source: https://habr.com/ru/post/1387215/


All Articles