Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of Scala by-name parameters

I am going through the book "Functional Programming in Scala" and have run across an example that I don't fully understand.

In the chapter on strictness/laziness the authors describe the construction of Streams and have code like this:

sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

object Stream {
    def cons[A](hd: => A, tl: => Stream[A]) : Stream[A] = {
        lazy val head = hd
        lazy val tail = tl
        Cons(() => head, () => tail)
    }
    ...
}

The question I have is in the smart constructor (cons) where it calls the constructor for the Cons case class. The specific syntax being used to pass the head and tail vals doesn't make sense to me. Why not just call the constructor like this:

Cons(head, tail)

As I understand the syntax used it is forcing the creation of two Function0 objects that simply return the head and tail vals. How is that different from just passing head and tail (without the () => prefix) since the Cons case class is already defined to take these parameters by-name anyway? Isn't this redundant? Or have I missed something?

like image 582
melston Avatar asked Oct 30 '14 21:10

melston


People also ask

What is the difference between a call-by-value and call by name parameter in Scala?

In Scala when arguments pass through call-by-value function it compute the passed-in expression's or arguments value once before calling the function . But a call-by-Name function in Scala calls the expression and recompute the passed-in expression's value every time it get accessed inside the function.

What is the difference between call by reference and call by name?

In the case of Call by Value, when we pass the value of the parameter during the calling of the function, it copies them to the function's actual local argument. In the case of Call by Reference, when we pass the parameter's location reference/address, it copies and assigns them to the function's local argument.

What do you understand by call by name?

Call by name is a a parameter passing scheme where the parameter is evaluated when it is used, not when the function is called.

Is Scala call by name?

For this circumstance, Scala offers call-by-name parameters. A call-by-name mechanism passes a code block to the call and each time the call accesses the parameter, the code block is executed and the value is calculated.


2 Answers

The difference is in => A not being equal to () => A.

The former is pass by name, and the latter is a function that takes no parameters and returns an A.

You can test this out in the Scala REPL.

scala> def test(x: => Int): () => Int = x
<console>:9: error: type mismatch;
 found   : Int
 required: () => Int
       def test(x: => Int): () => Int = x
                                        ^

Simply referencing x in my sample causes the parameter to be invoked. In your sample, it's constructing a method which defers invocation of x.

like image 70
Nate Avatar answered Nov 15 '22 05:11

Nate


First, you are assuming that => A and () => A are the same. However, they are not. For example, the => A can only be used in the context of passing parameters by-name - it is impossible to declare a val of type => A. As case class parameters are always vals (unless explicitly declared vars), it is clear why case class Cons[+A](h: => A, t: => Stream[A]) would not work.

Second, just wrapping a by-name parameter into a function with an empty parameter list is not the same as what the code above accomplishes: using lazy vals, it is ensured that both hd and tl are evaluated at most once. If the code read

Cons(() => hd, () => tl)

the original hd would be evaluated every time the h method (field) of a Cons object is invoked. Using a lazy val, hd is evaluated only the first time the h method of this Cons object is invoked, and the same value is returned in every subsequent invocation.

Demonstrating the difference in a stripped-down fashion in the REPL:

> def foo = { println("evaluating foo"); "foo" }
> val direct : () => String = () => foo
> direct()
evaluating foo
res6: String = foo
> direct()
evaluating foo
res7: String = foo
> val lzy : () => String = { lazy val v = foo; () => v }
> lzy()
evaluating foo
res8: String = foo
> lzy()
res9: String = foo

Note how the "evaluating foo" output in the second invocation of lzy() is gone, as opposed to the second invocation of direct().

like image 40
misberner Avatar answered Nov 15 '22 05:11

misberner