0

I have some code block like this:

object EntryPoint extends App {

  val arr = ArrayBuffer(1, 2, 3)
  doFirst(arr)

  def doFirst(a: ArrayBuffer[Int]) = {
    doSecond(s"$a")
  }

  def doSecond(
                 x: => String = ""
                 ) = {
    x match {
      case s: String => println(s"This is string: $s")
      case _ => println(x.getClass.getName)
    }
  }
}

Output: This is string: ArrayBuffer(1, 2, 3)

Why x evaluated like a string if in debugger i see lambda with 3 arguments? Is it because every call x is x.apply() ?

3
  • 1
    What did you expect? as soon as you reach x in your doSecond method, x is evaluated, and you're passing the toString representation of your ArrayBuffer. Commented Jun 6, 2022 at 8:56
  • Can you clarify what you see in debugger? Likely in the debugger you see some internal runtime representation of the by-name parameter. Commented Jun 6, 2022 at 9:35
  • @GaëlJ in debugger i saw EntryPoint$$Lambda with internal field arg. I'm interesting in possibility to get that arg value manually Commented Jun 6, 2022 at 10:24

1 Answer 1

2

A by-name parameter like x: => String is conceptually the same as a function with signature () => String

The following are equivalent and as you noticed already both parameters end up with the same runtime representation when debugging.

def doWithByNameParam(x: => String): String =
    x

def doWithFunction(x: () => String): String =
    x.apply()

doWithByNameParam("string")
doWithFunction(() => "string")

You just avoid some boilerplate with doWithByNameParam

Later Edit:

Here's what javap will give just so you understand what happens under the hood

  public static java.lang.String doWithByNameParam(scala.Function0<java.lang.String>);
  public static java.lang.String doWithFunction(scala.Function0<java.lang.String>);
Sign up to request clarification or add additional context in comments.

6 Comments

They're not the same, () => String is a function from Unit to String! : => String does not have an apply method with this signature: def apply(): String.
@AminMal check my response again. I've added a bit more context to my statement.
I see, but they're not equivalent to each other, at least in compile time. Try this, define val lazyFunc: () => String = () => "string", now if you pass this to doWithByNameParam, you'll get compile-time error, since the compiler expects a String, while you're passing a function0 of string. And also, you can pass a calculated String to doWithByNameParam, while you can't pass the calculated String to the other one obvoiusly! You can use lazy val's for the first one, you can't do it for the second one, and many other minor differences! just like how lazy values and functions differ.
Yes different types but I mentioned the differences in usage while obtaining the same behaviour. You can use lazy vals with both approaches without triggering its evaluation: lazy val x = ???; doWithFunction(() => x) As i was saying you avoid some boilerplate.
The last thing I'll say: for a beginner in my experience it's useful to see by-name params as Function0.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.