2

Say I have a Scala function :

def func(x:(Int,Int)):Int = x._1 + x._2

    func((1,2))  // This works as expected

But how come below function call also works correctly?

 func(1,2)

I know about function call being turned to object with apply methods but I am unable to see even then how this works?

try-catch-finally
  • 7,436
  • 6
  • 46
  • 67
ftw
  • 365
  • 2
  • 6
  • 16
  • In both Scala 2.11 and 2.12, the compiler warns: "Adapting argument list by creating a 2-tuple: this may not be what you want". The fact that you didn't mention this in your question makes me suspect you are not seeing or not paying attention to warnings, which isn't good practice. – Seth Tisue Dec 27 '16 at 16:50
  • @SethTisue I verified on both 2.11.8 and 2.12.1 (with and without `-deprecation`), and the compiler did not show any warnings in this case. Though it did show `warning: Adaptation of argument list by inserting () is deprecated: this is unlikely to be what you want.` when I did `List(1,2,3).toSet()`. – The_Tourist Dec 27 '16 at 18:11
  • 1
    Apologies, I forgot I have `-Xlint` enabled, which does make the warning show up. Enabling `-Xlint`, even in the REPL, is good practice. – Seth Tisue Dec 27 '16 at 21:49

2 Answers2

2

If there are no appropriate multi-argument methods and a single appropriate one-argument method, the Scala compiler will try to convert those comma separated arguments into tuples.

The type of the argument x to your func method is (Int, Int), which is a syntactic sugar for Tuple2[Int, Int]. So the signature of func method is actually func(Tuple2[Int, Int]).

You invoke it as func(1, 2), but there's no method with signature func(Int, Int) defined in the scope, so the compiler will roughly translate the invocation to func(Tuple2(1, 2)), which matches the signature of your method. So this kind of invocation will work, but can lead to unexpected results (it's not hard to see why).

EDIT: Also see this question for additional reading.

Community
  • 1
  • 1
The_Tourist
  • 2,048
  • 17
  • 21
  • Whats the need to do that?Was there any specific reason this extra check was implemented? – ftw Dec 27 '16 at 05:38
  • It's not an extra check, just a compiler hack which you can verify from the bytecode that gets generated. I can't point you to the exact sources, but I believe this kind of usage is not recommended. – The_Tourist Dec 27 '16 at 05:49
  • It may be an artifact of the long ago abandoned effort to unify argument lists and tuples. – Jörg W Mittag Dec 27 '16 at 14:45
0

This is a syntax of scala:

(x_1 , … , x_n),((x_1 , … , x_n))is a shorthand for `Tuple$n$($x_1 , … , x_n$)

check this Tuples, revised.

and also when check the generated bytecode:

scala> def bar(x: Int, y: Int) = func(x, y)
scala> :javap -c bar
Compiled from "<console>"
public class $line5.$read$$iw$$iw$ {
  public static $line5.$read$$iw$$iw$ MODULE$;

  public static {};
    Code:
       0: new           #2                  // class $line5/$read$$iw$$iw$
       3: invokespecial #23                 // Method "<init>":()V
       6: return

  public int bar(int, int);
    Code:
       0: getstatic     #30                 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
       3: new           #32                 // class scala/Tuple2$mcII$sp
       6: dup
       7: iload_1
       8: iload_2
       9: invokespecial #35                 // Method scala/Tuple2$mcII$sp."<init>":(II)V
      12: invokevirtual #39                 // Method $line3/$read$$iw$$iw$.func:(Lscala/Tuple2;)I
      15: ireturn

  public $line5.$read$$iw$$iw$();
    Code:
       0: aload_0
       1: invokespecial #42                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: putstatic     #44                 // Field MODULE$:L$line5/$read$$iw$$iw$;
       8: return
}

we can see this is transformed by compiler: new #32 // class scala/Tuple2$mcII$sp

and I think this is equivalent to Function.untupled, example:

scala> Function.untupled(func _)(1, 2)
res1: Int = 3
chengpohi
  • 14,064
  • 1
  • 24
  • 42