I am a bit unclear on where and how what appears to be a type conversion takes place in the following example. Let say I define function (all shown in ammonite shell)
@ def f(x: Seq[Double]): Double = x.sum
defined function f
Then following works
@ f(Seq(1, 2, 3))
res4: Double = 6.0
But this one does not
@ val s = Seq(1, 2, 3)
s: Seq[Int] = List(1, 2, 3)
cmd11.sc:1: type mismatch;
found : Seq[Int]
val s = Seq(1, 2, 3); val res11_1 = f(s)
So the question is where is this type conversion defined, why it does not work in the second case and how can it be made to work in the second case?
Inference goes both ways, so if a method takes
Seq[Double] and you give it a
Seq without explicit type then compiler infers
Seq[Double] and converts numbers to Doubles. In second case you’ve separated the steps, so when inferring the type of
val s the compiler doesn’t yet take into consideration that it will be passed to
def f. Therefore it infers
Seq[Int] and that can’t be passed to
The solution could be explicit typing, i.e.:
val s = Seq[Double](1, 2, 3)
f(s) // works
val s = Seq[Double](1, 2, 3)
val s = Seq(1.0, 2.0, 3.0)
val s: Seq[Double] = Seq(1, 2, 3)
val s = Seq(1, 2, 3): Seq[Double]
All of them result in
It should be noted that while the compiler does some backwards inference, it is very limited. For example, while
f(Seq(1, 2, 3)) works,
f(Seq(1, 2, 3) :+ 4) already fails.
Numeric literals are processed in the context of an expected type. If no particular type is expected, then e.g.
1 is inferred to be an
Int. But if a
Double is expected,
1 is treated as if you had written
val s = Seq(1, 2, 3) no particular type is expected, so first
3 are assumed to be
Int, and that causes the expression as a whole to have
f(Seq(1, 2, 3)), the compiler knows that
f expects a
Seq(1, 2, 3) is expected to be that type, and that in turn causes
3 to have an expected type of
This is a really good pair of examples for understanding the bidirectional nature of type inference. Expected types flow inward; inferred types flow outward.
@tarsa’s explanation is correct, but one additional detail is worth noting: this isn’t conventional type conversion, it’s a very special edge case called “numeric widening” – you can find the technical definition in the Scala spec.
It’s very precise but a little ad-hoc – one of those things that got added to the language because people intuitively tend to want it, but less elegant than we might wish. There have been occasional proposals to make this something more general and well-defined, but they haven’t gone anywhere. As a result, Scala 3 will probably be restricting the idea to just the common case you show here – allowing Integer literals to be used as other types where that makes sense, but nothing else.
Than you all. It is all clear now. Nevertheless it does feel some unsatisfying but I guess there not much we can do about it.