为什么Scala会自动应用thunk?

时间:2010-05-29 09:56:55

标签: syntax scala functional-programming

ShadowofCatron Scala Tutorial 3 video的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
    }

}

为了清楚这个问题,这个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

在访问此网站询问此问题后,我遇到了this answer,它告诉我如何解决上述Scala计划:

    val b = f _     // Hey Scala, I mean f, not f()

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

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

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

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

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


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

4 个答案:

答案 0 :(得分:14)

问题在于:

  

Buh?“我的功能编程说   大脑,因为一个功能的价值   以及它评估时的值   应用完全不同   的东西。

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

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

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

val b = f _     // Hey Scala, I mean f, not f()

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

val b = () => f()

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

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

答案 1 :(得分:13)

编写时的默认原因:

val b = f

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

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

答案 2 :(得分:12)

在你的例子中

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

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

val f = () => { counter = counter + 1; counter }

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

答案 3 :(得分:9)

您的猜测是正确的 - Scala在表达式的评估方面具有类型相关的语义。

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

但是由于Scala具有强大的静态类型系统,因此可以打破上述规则,并保护程序员不会在评估结果无意义的情况下明确部分应用这些函数一种类型的观点。

请注意,依赖于类型的评估甚至可以模拟call-by-name


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

相关问题