Scala Call By Name Confusion

前端 未结 3 1292
渐次进展
渐次进展 2021-01-05 16:48

I am working on some call by name examples using the REPL and running the same examples in Eclipse.

Here is what in Eclipse:
Scenario 1:

val fun         


        
3条回答
  •  谎友^
    谎友^ (楼主)
    2021-01-05 17:26

    Updated after @IttayD's answer:

    The scenario 1 on Eclipse is right, you'll see why below. Scenario 2 is clearly an Eclipse bug. ScalaIDE on Eclipse is known for its brokenness. I wouldn't trust it (or use it). Use Intellij IDEA's Scala plugin if you must.

    The answer to both of your questions is that, {} is a block that returns the return type of it's last statement. It's exactly the same as Scheme's (begin) or Common Lisp's (progn). When you have:

    scala> def takesFunct(f: => Unit)
    {
      val b = f
    }
    takesFunct: (f: => Unit)Unit
    
    scala> val funct = {println("Calling funct")}
    Calling funct
    funct: Unit = ()
    
    scala> takesFunct(funct)
    // No Output
    

    funct's RHS has already been eagerly evaluated and returned a value () of type Unit to funct. Applying an already computed value to a call-by-name function and using it in the body doesn't cause reevaluation because the value is already a leaf.

    Further Update:

    def takesFunct(f: => Unit)
    

    has essentially the same semantics as

    def takesFunct(f: () => Unit)
    

    which is known as streaming or delayed evaluation in certain circles. There is one major difference though, which lies in the way you invoke the supplied argument. In the latter case, in order to get back a value from f, you have to invoke it as such - i.e f(). In the former case, f is a lazy expression that evaluates to a value when it is first referenced as such, hence call-by-name. You can think of the syntax f: => Unit as a way to automatically wrap whatever expression you supply in a container {}. The contents of which is retrieved when used like so:

    scala> val a = { 1 } // 1 wrapped in {}, and retrieved when assigned to a
    a: Int = 1
    

    So what about this?

    scala> takesFunct({println("Calling funct")})
    Calling funct
    

    This is because now you are creating a block in-place that is bound to the function's parameter f, and it is only evaluated when you use it in val b = f. Let's do one more experiment:

    scala> takesFunct(println("Calling funct"))
    Calling funct
    

    How come you ask? Because println(...) was wrapped in a {} that is bound to f. Referencing f retrieves the value inside the container, which is the value of println(...), which is ():Unit. In the previous example, f was bound to { { println(...) } }, which is the same as { println(...) }, so you get the same result. In fact you can nest {} indefinitely and still get the same thing back. The only difference is, manually supplying {} lets you put multiple statements inside like so:

    scala> takesFunct({ println("hello"); println("world") })
    hello
    world
    

    Hope this helps.

提交回复
热议问题