代码之家  ›  专栏  ›  技术社区  ›  Anonymoose

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

  •  26
  • Anonymoose  · 技术社区  · 15 年前

    刚过2:40英寸 ShadowofCatron Scala Tutorial 3 video ,它指出 名称后的括号 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
        }
    
    }
    

    为了澄清这个问题,这个方案程序(以及下面的控制台转储)显示了我期望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()
    

    但只需要下划线“hint” 有时 . 当我呼唤 runThunk(f) ,不需要提示。但当我用A“化名”F到B时 val 然后应用它,它不起作用:应用程序发生在 瓦尔 甚至 lazy val 以这种方式工作,所以这不是导致这种行为的评估点。

    这一切留给我的问题是:

    为什么斯卡拉 有时 评估时自动应用thunk?

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

    这是个好主意吗?scala程序员是否应用thunk而不是引用其值? 更多的时候 使parens可选总体上更好?


    在R5RS中使用scala 2.8.0rc3和drscheme 4.0.1编写的示例。

    4 回复  |  直到 15 年前
        1
  •  14
  •   Daniel C. Sobral    15 年前

    问题在于:

    布赫?”说我的函数式编程 大脑,因为功能的价值 以及它计算的值 应用完全不同 东西。

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

    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样式中,它们没有任何参数(例如, length size )因此,为了使代码在空参数列表方面看起来更统一,scala使它们成为可选的。

        2
  •  13
  •   Jesper    15 年前

    默认原因,当您写入时:

    val b = f
    

    是评估函数并将结果分配给 b 正如你所注意到的。你可以使用 _ 或者可以显式指定 :

    // These all have the same effect
    val b = f _
    val b: () => Int = f
    val b: Function0[Int] = f
    
        3
  •  12
  •   gerferra    15 年前

    在你的例子中

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

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

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

    我想你会得到你想要的。

        4
  •  9
  •   Matthew Flaschen    15 年前

    您的猜测是正确的-scala对于表达式的计算具有类型相关的语义。

    像露比一样 总是 即使没有括号,也计算thunk。(这对于交互来说可能有好处,因为您可以切换纯操作,也可能是不纯操作,而不必更改语法。)

    但是由于scala有一个强大的静态类型系统,所以 可以 打破上述规则,避免程序员在从类型角度看计算结果不合理的情况下显式地部分应用函数。

    注意,依赖类型的计算甚至可以模拟 call-by-name


    现在依赖类型的评估行为是好是坏?…好吧,这肯定会导致像你这样的案件令人困惑,而且感觉不到 纯净的 再。但在大多数情况下, 它只是工作 正如程序员所预期的那样(使代码更简洁)-所以我们假设,它是 可以 .