Pham
Pham

Reputation: 1225

Scala Tuple not show type mismatch if passing parameter inline

I have an method that expects (String, Double) as parameter

scala> def testTupleType(x: (String, Double)) = {}
testTupleType: (x: (String, Double))Unit

scala> val x: Int = 2
x: Int = 2

Then I try to pass the parameter to testTupleType method like that:

scala> val testTuple = ("String", x)
testTuple: (String, Int) = (String,2)

scala> testTupleType(testTuple)
<console>:11: error: type mismatch;
 found   : (String, Int)
 required: (String, Double)
              testTupleType(testTuple)

It show the error as expected. But when I pass the parameter inline:

 scala> testTupleType(("String", x))

Do not have any error! I do not know is this because of an implicit conversion here?

Upvotes: 1

Views: 695

Answers (3)

4lex1v
4lex1v

Reputation: 21567

To understand the problem let's look at what -Xprint:typer shows:

In the first case you are passing a testTuple: (String, Int) = (String,2) into the method which expects: testTupleType: (x: (String, Double))Unit, but Scala doesn't have an implicit convertion from Tuple2[String, Int] to Tuple2[String, Double]:

val t = Tuple2[String, Int]("a", 2)
val a: (String, Double) = t
<console>:8: error: type mismatch;
 found   : (String, Int)
 required: (String, Double)
       val a: (String, Double) = t
                                 ^

Remember that (String, Int) is just a syntactic sugar for the compiler which calls Tuple2.apply.

So why the seconds snippets works? Because Scala desugars this into the Tuple2[String, Double]:

private[this] val res1: Unit = testTupleType(scala.Tuple2.apply[String, Double]("String", x.toDouble));
 <stable> <accessor> def res1: Unit = res1

To summaries, in the first case you already have a Tuple2[String, Int] and Scala doesn't have an implicit convertion for it to Tuple2[String, Double], but you can make your own:

implicit def tsi2tdi(t: Tuple2[String, Int]): Tuple2[String, Double] = (t._1, t._2.toDouble)

and in the seconds case it sees the you have a String and an Int and you need to convert them into Tuple2[String, Double].

Update

No, it's called not by the apply method, it's called by the compiler. While compiling Scala performs different analysis phases, one of which is called typer. When scalac sees then you have an Int in the code, but what you want to get is a Double value, it knows that, according to the Weak Conformance rules mentioned by senia, it can get a Double from Int by calling toDouble method. That's not an implicit convertion in Scala point of view. If you remember primitive convertions were in C and Java, long before Scala

Upvotes: 2

senia
senia

Reputation: 38045

There is no implicit conversion from Int to Double in scala. See 3.5.3 Weak Conformance in SLS.

In case of testTupleType(testTuple) error contains all information: (String, Int) is not a (String, Double).

With "inline" parameters:

testTupleType(("String", x))

It means:

testTupleType(Tuple2.apply("String", x))

Method apply accepts 2 type parameters:

def apply[T1, T2](_1: T1, _2: T2): (T1, T2)

testTupleType accepts (String, Double), so the result type of apply ((T1, T2)) should be (String, Double). After type inference:

testTupleType(Tuple2.apply[String, Double]("String", x))

or:

val a: (String, Double) = Tuple2.apply[String, Double]("String", x)
testTupleType(a)

Due to weak conformance you can use Int as Double here.

Upvotes: 4

korefn
korefn

Reputation: 955

The problem is due to your code depending on implicit conversion in which case it does not happen when you pass the testTuple inline.

If you use val x:Double = 2 it should work.

Upvotes: 1

Related Questions