Reputation: 1225
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
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
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
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