为什么Scala会自动应用thunk?

Ano*_*ose 27 syntax functional-programming scala

ShadowofCatronScala Tutorial 3视频中,在2:40之后,它指出了一个thunk名称后面的括号是可选的."寮步?" 说我的函数式编程大脑,因为函数的值和它在应用时评估的值是完全不同的东西.

所以我写了以下内容来试试这个.我的思考过程在评论中有所描述.

object Main {

    var counter: Int = 10
    def f(): Int = { counter = counter + 1; counter }

    def runThunk(t: () => Int): Int = { t() }

    def main(args: Array[String]): Unit = {
        val a = f()     // I expect this to mean "apply f to no args"
        println(a)      // and apparently it does

        val b = f       // I expect this to mean "the value f", a function value
        println(b)      // but it's the value it evaluates to when applied to no args
        println(b)      // and the application happens immediately, not in the call

        runThunk(b)     // This is an error: it's not println doing something funny
        runThunk(f)     // Not an error: seems to be val doing something funny
    }

}
Run Code Online (Sandbox Code Playgroud)

 

为了清楚这个问题,这个Scheme程序(以及后面的控制台转储)显示了我对Scala程序的期望.

(define counter (list 10))
(define f (lambda ()
            (set-car! counter (+ (car counter) 1))
            (car counter)))

(define runThunk (lambda (t) (t)))

(define main (lambda args
               (let ((a (f))
                     (b f))
                 (display a) (newline)
                 (display b) (newline)
                 (display b) (newline)
                 (runThunk b)
                 (runThunk f))))

> (main)
11
#<procedure:f>
#<procedure:f>
13
Run Code Online (Sandbox Code Playgroud)

 

来到这个网站询问这个问题后,我遇到了这个答案,告诉我如何修复上面的Scala程序:

    val b = f _     // Hey Scala, I mean f, not f()
Run Code Online (Sandbox Code Playgroud)

有时只需要下划线'提示' .当我打电话时runThunk(f),不需要提示.但是,当我用'a'替换'到b val然后应用它时,它不起作用:应用程序发生在val; 甚至以lazy val这种方式工作,所以这不是导致这种行为的评估点.

 

这一切都让我想到了这个问题:

为什么Scala 在评估它们时有时会自动应用thunk?

正如我怀疑的那样,是类型推断吗?如果是这样,类型系统不应该超出语言的语义吗?

这是一个好主意吗?Scala程序员是否应用thunk而不是更频繁地引用它们的值,使得parens可选的整体更好?


在R5RS中使用Scala 2.8.0RC3,DrScheme 4.0.1编写的示例.

Dan*_*ral 15

问题出在这里:

嗯?"我的函数编程大脑说,因为函数的值和它在应用时评估的值是完全不同的东西.

是的,但你没有声明任何功能.

def f(): Int = { counter = counter + 1; counter }
Run Code Online (Sandbox Code Playgroud)

您声明了一个名为call的方法f,该方法有一个空参数列表,并返回Int.方法不是函数 - 它没有值.永远不能.你能做的最好的事情就是Method通过反射得到一个实例,这根本不是一回事.

val b = f _     // Hey Scala, I mean f, not f()
Run Code Online (Sandbox Code Playgroud)

那么,f _意味着什么?如果f是一个函数,它将意味着函数本身被授予,但这不是这里的情况.它的真正含义是:

val b = () => f()
Run Code Online (Sandbox Code Playgroud)

换句话说,f _是方法调用的闭包.闭包是通过函数实现的.

最后,为什么Scala中的空参数列表是可选的?因为虽然Scala允许声明,例如def f = 5,Java却没有.Java中的所有方法至少需要一个空参数列表.并且有很多这样的方法,在Scala风格中,没有任何参数(例如,lengthsize).因此,为了使代码在空参数列表方面看起来更加统一,Scala使它们成为可选的.


Jes*_*per 14

写入时的默认原因:

val b = f
Run Code Online (Sandbox Code Playgroud)

b正如您所注意到的那样,是评估函数并将结果分配给.您可以使用_,或者您可以明确指定类型b:

// These all have the same effect
val b = f _
val b: () => Int = f
val b: Function0[Int] = f
Run Code Online (Sandbox Code Playgroud)


ger*_*rra 12

在你的例子中

def f(): Int = { counter = counter + 1; counter }
Run Code Online (Sandbox Code Playgroud)

定义一个方法,而不是一个函数.根据上下文,AFAIK方法在Scala中自动升级为函数.要定义一个可以编写的函数

val f = () => { counter = counter + 1; counter }
Run Code Online (Sandbox Code Playgroud)

我想你会得到你想要的东西.

  • @Anonymouse这不是真的正确.声明中方法`f`返回的值是`Int`,正如您指定的那样.如果你声明`def f():()=> Int =()=> {counter = counter + 1; counter}`,然后你有一个返回函数的方法.仍然,`{counter = counter + 1; counter}`是_not_匿名函数.它是一个块,它可以计算出什么是`counter`的类型.所以这不是一个`val` vs`def`的东西 - 你必须记住方法不是函数. (2认同)

Dar*_*rio 10

您的猜测是正确的 - Scala具有与表达式求值相关的类型相关语义.

像Ruby一样,它总是在没有括号的情况下评估thunk.(这可能有利于交互目的,因为您可以在不必更改语法的情况下切换纯操作和可能不纯的操作.)

但是由于Scala具有强大的静态类型系统,它可以打破上述规则,并且在程序从类型的角度来看评估结果没有意义的情况下,保存程序员不会明确地部分应用这些函数.

请注意,依赖于类型的评估甚至可以模拟按名称调用


现在依赖于类型的评估行为是好还是坏?......好吧,它肯定会导致像你这样令人困惑的案件,并且不再感到纯粹.但在大多数情况下,它只是按程序员的意图运行(使代码更简洁) - 所以,让我们说,没关系.