开发者

Scala Call By Name Confusion

开发者 https://www.devze.com 2023-02-10 01:35 出处:网络
I am working on some call by name examples using the REPL and running the same examples in Eclipse. Here is what in Eclipse:

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 same

takesFunct({println("Calling funct")}

The output is:

Calling funct

Calling funct

Scala 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 in

val 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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号