Syntax 为什么Scala有时会自动应用thunks?

Syntax 为什么Scala有时会自动应用thunks?,syntax,scala,functional-programming,Syntax,Scala,Functional Programming,在世纪的2:40之后,有人指出a名称后面的括号是可选的。“Buh?”我的函数编程大脑说,因为一个函数的值和它应用时的评估值是完全不同的 所以我写了下面的内容来尝试一下。我的思考过程在评论中有描述 object Main { var counter: Int = 10 def f(): Int = { counter = counter + 1; counter } def runThunk(t: () => Int): Int = { t() } de

在世纪的2:40之后,有人指出a名称后面的括号是可选的。“Buh?”我的函数编程大脑说,因为一个函数的值和它应用时的评估值是完全不同的

所以我写了下面的内容来尝试一下。我的思考过程在评论中有描述

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
但下划线“提示”只是有时需要。当我调用
runThunk(f)
时,不需要任何提示。但是当我用
val
将f“别名”为b,然后应用它时,它不起作用:应用程序发生在
val
中;即使是
lazy val
也是这样工作的,因此这不是导致这种行为的评估点

这就给我留下了一个问题:

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

我怀疑这是类型推断吗?如果是这样的话,类型系统不应该远离语言的语义吗

这是个好主意吗?Scala程序员是否更频繁地应用Thunk而不是引用它们的值,从而使Paren可选总体上更好



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

您的猜测是正确的-Scala在表达式求值方面具有依赖于类型的语义

与Ruby一样,它总是计算thunks,即使没有括号。(这对于交互目的可能有好处,因为您可以在不更改语法的情况下切换纯操作和可能不纯操作。)

但由于Scala有一个强大的静态类型系统,它可以打破上述规则,避免程序员在从类型角度看评估结果没有意义的情况下显式地部分应用函数

注意,依赖类型的求值甚至可以模拟



现在,依赖类型的评估行为是好是坏。。。好吧,这肯定会导致像你这样令人困惑的案件,而且感觉不再那么纯粹了。但在大多数情况下,它只是按照程序员的预期工作(使代码更加简洁)-所以我们可以说,这没关系。

当您编写以下代码时,默认原因是:

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

// These all have the same effect
val b = f _
val b: () => Int = f
val b: Function0[Int] = f
在你的例子中

def f(): Int = { counter = counter + 1; counter }
定义的是方法,而不是函数。AFAIK方法在Scala中根据上下文自动升级为函数。要定义一个函数,可以编写

val f = () => { counter = counter + 1; counter }
我想你会得到你想要的。

问题在于:

“嗯?”我的函数式编程说 大脑,因为有一个功能的价值 以及当 他们完全不同 事情

是的,但是您没有声明任何函数

def f(): Int = { counter = counter + 1; counter }
您声明了一个名为
f
的方法,该方法有一个空参数列表,并返回
Int
。一个方法不是一个函数,它没有值。永远不会。您所能做的最好的事情就是通过反射获得一个
方法
实例,这实际上根本不是一回事

val b = f _     // Hey Scala, I mean f, not f()
那么,
f
是什么意思呢?如果
f
是一个函数,它将意味着函数本身,当然,但这里不是这样。它真正的意思是:

val b = () => f()
换句话说,
f 
是方法调用上的闭包。闭包是通过函数实现的


最后,为什么空参数列表在Scala中是可选的?因为虽然Scala允许声明,如
def=5
,但Java不允许。Java中的所有方法都至少需要一个空参数列表。而且有许多这样的方法,在Scala样式中,不会有任何参数(例如,
length
size
)。因此,为了使代码在空参数列表方面看起来更加统一,Scala将其设置为可选的。

+1这是一个写得很好的问题。啊哈,这就解释了!所以您要说的是,创建匿名函数值实际上创建了一个函数值(然后它被命名为
val
),而看起来像是定义快捷方式(例如
(定义(f args)主体)
,用于Scheme)在Scala中做了一些等价的事情,但在JVM级别上做了一些完全不同的事情。谢谢。这正是我想知道的。@Anonymouse这不是真的正确。声明中方法
f
返回的值是
Int
,正如您指定的。如果您声明
def():()=>Int=>{counter=counter+1;counter}
,那么您就有了一个返回函数的方法不是匿名函数。它是一个计算为
计数器
类型的块。因此,这不是
val
vs
def
的事情——您只需记住方法不是函数。
val b = () => f()