2

Can somebody explain me why the r1 type is: (String => String, String)however r2 type is String => (String, String)? Thank you.

def f1(n: String, m: String): String = m + n
val f2: String => String = f1(_, "something")
val r1: (String => String, String) = f2 -> "foo"
val r2: String => (String, String) = f1(_, "something") -> "foo"
Jatin
  • 31,116
  • 15
  • 98
  • 163
limuhob
  • 23
  • 1
  • 4

2 Answers2

3

Lets see what happens in place holder syntax for anonymous functions:

val f2: String => String = f1(_, "something")

It is expanded as: (x$1: String) => f1(x$1, "something")" (Start your repl with scala -Xprint:typer)

With f2 -> "foo", it simply becomes (f2,"foo") and hence (String => String, String)

With f1(_, "something") -> "foo", it is evaluated as:

(x:String) => f1(x,"something") -> foo
(x:String) => (f1(x,"something") , foo)
(String => (String, String))

If the confusion on why placeholder is evaluated first?

Placeholder is evaluated and the tree is adjusted at compile time. Where as with ->, it gets evaluated at runtime as tuple a due to ArrayAssoc implicit.

Community
  • 1
  • 1
Jatin
  • 31,116
  • 15
  • 98
  • 163
0

Certain things were confusing me even after reading Jatin's answer. Here are my findings after some further research. Note, to save typing, am not using Type ascriptions on left hand side and let Scala infer stuff.

def f1(n: String, m: String): String = m + n

// f1: (n: String, m: String)String

val f2 = f1(_, "something") 

Generally, underscores in an "expression" represent anonymous functions which are expanded appropriately by the compiler. If the compiler can not find a suitable type for the 'underscore' parameter, it will complain like below:

// <console>:12: error: missing parameter type for expanded function ((x$1) => f1(x$1, "something"))    

val f2 = f1(_:String, "something") // Specifiying the type of the `_` as `_:String` fixes the missing parameter type error above.

// f2: String => String = <function1> 

val r1 = f2 -> "foo"
// r1: (String => String, String) = (<function1>,foo) 

Now the important stuff. Why does not the below line give the same result as r1 above!!! The reason lies in excellent answer by Daniel at underscore scoping rules.

val r2 = f1(_:String, "something") -> "foo"
// r2: String => (String, String) = <function1>

According to the 1st rule in the answer by Daniel, the scope of the anonymous function will include the whole right side expression. So the expanded anonymous function above will be

(x:String) => f1(x:String, "something") -> "foo"

Which gives the function signature String => (String, String)

In order to fix that, we use the 2nd rule in Sobral's answer and limit the scope of the anonymous function bound to the _ by enclosing the f1 expression with () or {}, like below:

val r3 = (f1(_:String, "something")) -> "foo"
r3: (String => String, String) = (<function1>,foo)

Now, we get the same result as val r1 = f2 -> "foo"

Community
  • 1
  • 1
Samar
  • 2,091
  • 1
  • 15
  • 18