What are the rules to govern underscore to define anonymous function?

风流意气都作罢 提交于 2019-11-28 10:11:03

Simple rules to determine the scope of underscore:

  1. If the underscore is an argument to a method, then the scope will be outside that method, otherwise respective the rules below;
  2. If the underscore is inside an expression delimited by () or {}, the innermost such delimiter that contains the underscore will be used;
  3. All other things being equal, the largest expression possible will be used.

So, by the rule #1, instead of println((x: Int) => x), the scope will be placed outside (including) println.

By rule #2, the latter two examples will have the function delimited by parenthesis, so (x => println(x: Int)).

By rule #3, the first example will be the whole expression, as there are no delimiting parenthesis.

I believe Mr. Sobral's answer is incorrect. The actual rules can be found in Scala Language Reference, section 6.23, subhead "Placeholder Syntax for Anonymous Functions."

The only rule is that the innermost expression that properly contains the underscore defines the scope of the anonymous function. That means that Mr. Sobral's first two rules are correct, because a method call is an expression and parenthesizing an expression doesn't change its meaning. But the third rule is the opposite of the truth: all other things being equal, the smallest expression that makes sense will be used.

Unfortunately, my explanation for the behavior Mr. Laskowski observed for his first example is a bit involved and speculative. When

List(1,2,3) foreach println(_:Int)

is typed at the Scala read-eval-print loop. The error message is:

error: type mismatch;
 found   : Unit
 required: Int => ?
              List(1,2,3) foreach println(_:Int)
                                         ^

If you vary the example a tiny bit:

List(1,2,3).foreach println(_:Int)

the error message is easier to make sense of --

error: missing arguments for method foreach in class List;
follow this method with `_' if you want to treat it as a partially applied function
          List(1,2,3).foreach println(_:Int)
                      ^

To understand things a little better, call scala thus: scala -Xprint:parser, which, after every expression is typed by the user, causes the expression as fleshed out by the parser to be printed. (Along with a lot of garbage, which I'll omit.) For Laskowski's first example, the expression understood by the parser is

((x$1: Int) => List(1, 2, 3).foreach(println((x$1: Int))))

For the second example, the parser's version is

((x$1: Int) => List(1, 2, 3).foreach.println((x$1: Int)))

Apparently the scope rule is applied before the expression structure has been fully fleshed out. In both cases, the parser guesses that the smallest expression starts at List, even though once the parens are inserted that's no longer true. In the second example, in addition to that assumption it assumes that, because println is an identifier, foreach println is a chain of methods, the first having no arguments. The error at foreach is then caught before the error at println, masking it. The error at println is that its result is Unit, and foreach requires a function. Once you see the parse tree, it's easy to see that this is correct, but it's not clear (to me) why the parse tree is what it is.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!