Partial function application prematurely runs codeblock when used with underscore

江枫思渺然 提交于 2019-12-01 00:11:12

问题


Given:

def save(f: => Any)(run:Boolean) { if (run) { println("running f"); f } else println("not running f") } 

I can call it with:

save("test")(true) -> running f
save("test")(false) -> not running f
save(throw new RuntimeException("boom!"))(false) -> not running f
save(throw new RuntimeException("boom!"))(true) -> running f and then exception thrown

Here's the curious behaviour with partial application:

save(throw new RuntimeException("boom!"))(_) -> (Boolean) => Unit = <function1> //as expected
save(throw new RuntimeException("boom!")) _ -> exception thrown

The codeblock is evaluated immediately without being passed in as a function. What is the difference between the above 2 statements?


回答1:


First case,

save(throw new RuntimeException("boom!")) _ 

According to "Scala Reference" (§6.7), trailing underscore is used in place of the argument list, and expression is converted to

val f: (Boolean) => Unit = save(throw new RuntimeException("boom!"))

where the first argument of def save is immediately evaluated.

The expression e _ is well-formed if e is of method type or if e is a call-by-name parameter. If e is a method with parameters, e _ represents e converted to a function type by eta expansion (§6.26.5). If e is a parameterless method or call-by-name parameter of type =>T , e _ represents the function of type () => T , which evaluates e when it is applied to the empty parameterlist ().

To make the things work as you expect, some modifications are required:

scala> def save(f:() => Any)(run:Boolean) { if (run) { println("running f"); f() } else println("not running f") }
save: (f: () => Any)(run: Boolean)Unit

scala> val f = save(() => throw new RuntimeException("boom!")) _
f: (Boolean) => Unit = <function1>

scala> f(true)
running f
java.lang.RuntimeException: boom!
        at $anonfun$1.apply(<console>:6)

Second case,

save(throw new RuntimeException("boom!"))(_)

According to "Scala Reference" (§6.23), when placeholder is used as a replacement for an argument, the expression is converted to

val f: (Boolean) => Unit = save(throw new RuntimeException("boom!"))(_)



回答2:


The behaviour of call-by-name parameters under eta expansion is currently under review, see this bug. Your code works as you expect (that is, the line save(throw new RuntimeException("boom!")) _ returns a function without throwing the exception) with recent nightly builds of 2.10. Let's see if it stays in until release!

See also this question for a related question on the general case of eta expansion not involving call-by-name.



来源:https://stackoverflow.com/questions/4918362/partial-function-application-prematurely-runs-codeblock-when-used-with-underscor

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