Scala Call By Name Confusion
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 funct = {println("Calling funct")}
takesFunct(funct)
def takesFunct(f: => Unit)
{
val b = f
}
The output is:Calling funct
Scenario 2:
the method takesFunct stays the sametakesFunct({println("Calling funct")}
The output is:
Calling funct Calling functScala REPL Scenario 1:
scala> def takesFunct(f: => Unit)
{
val b = f
}
takesFunct: (f: => Unit)Unit
scala> val funct = {println("Calling funct")}
Calling funct
funct: Unit = ()开发者_如何学Go
scala> takesFunct(funct)
// No Output
Scenario 2 The same method as defined above
scala> takesFunct({println("Calling funct")}
Calling funct
Two questions
1) Why is the output from Eclipse different from the REPL? 2) What is the difference between passing inval funct = {...}
takesFunct(funct)
as opposed to
takesFunct({...})
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.
There is no difference in the output. The difference is in what you want. From Eclipse you ran two things:
val funct = {println("Calling funct")} // prints Calling funct here
takesFunct(funct)
def takesFunct(f: => Unit)
{
val b = f
}
and
val funct = {println("Calling funct")} // prints Calling funct here
takesFunct({println("Calling funct")}
def takesFunct(f: => Unit)
{
val b = f // prints Calling funct here
}
On the REPL, the same thing happened according to your own logs:
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
Now, in the second scenario all you did was this:
scala> takesFunct({println("Calling funct")}
Calling funct
Since you did not repeat the val funct
assignment, which was still present in Eclipse's second scenario, it did not print a message.
Note that
val funct = {println("Calling funct")}
is, as a practical matter, equivalent to
println("Calling funct")
val funct = ()
Scenario 1: As expected. The output is from the first line, not calling the method.
Scenario 2: I don't have Eclipse, but doesn't sound right.
REPL Scenario 1: you can't define a call by name value. what you did is assign the last value of the expression {println("Calling funct")}
to funct. That last value is the result of println, which is Unit. So takesFunct receives a thunk that evaluates to just Unit, so nothing is printed.
REPL Scenario 2: As expected
精彩评论