case class Text(number: Int, line: String)
object Main {
val a = Text(42,"The answer")
def main(args: Array[String]): Unit =
{ println(s"The number is '${a._1}'")
println(s"The line is '${a._2}'") } }
is this intentional or a bug? Btw, for normal classes it does not compile on Scala 3.1.1
Fascinating. The question, mind, is why this is working at all in Scala 3. I mean, there’s no reason why it should work, and I’m totally unsurprised that it fails in Scala 2. So this has to be a side-effect of some enhancement in Scala 3.
I would guess that it is coming out of the parameter untupling changes, but that’s just a guess based on two minutes of research, and I’m not at all sure that it’s plausible.
This is intentional. Case classes in Scala 3 define _N fields, combined with an unapply method that returns the case class instance itself this allows for pattern matching without unnecessary allocations (Scala 2 was also capable of matching on case classes without allocating, but this was done by special-casing case classes, whereas in Scala 3 it’s just a consequence of how they’re desugared)
Neat – I was wondering if it might be something of the sort. Is this enhancement spelled out anywhere in the Dotty docs? It’s a nice little detail, and seems to be pretty obscure currently…
So, shouldn’t this be identified and documented in the “Transition from 2.x to 3.x Document” (cannot remember its official name) which explains all the differences?
And I find it fascinating there was no code in the huge testing corpus that didn’t use unapply. And if there is, then why didn’t those tests fail?!
This was a helpful discussion, thanks for all the answers. I was suspecting it could be intentional, but was unsure, because i thought i read somewhere that the old style tuple accessors were later phased out. But now i assume they are not.
I think we’re talking about two different things here – I’m referring to the new built-in ._1, ._2 feature, which isn’t documented yet. The rewrites to unapply were extensively documented, as @smarter linked to above.
As to the change: honestly, the pattern you show is kind of obscure – I didn’t even know it could be done that way in Scala 2. (I don’t recall seeing it documented anywhere.) And there are better ways to accomplish the same goal in Scala 3. So yes, it’s a language change, but seems like one of the more minor ones…
I think it’s the other way around – this never worked for case classes before, but now it does. (That is, there is no “old style” about it.) Do you have reason to believe that tuple accessors worked here before Scala 3?
I am very happy to see the ._1 and ._2 feature was added. I hope it gets documented somewhere soon. I would have happily used the older pattern when transitioning my Scala 2 codebase, and been quite frustrated hitting this particular issue.
And I am honestly very glad to hear it is obscure. It wasn’t and isn’t for me and my codebase(s).
As such, it would still be nice to have this pattern “called out” as a migration issue guiding me to the meta and then the proper way to approach it in Scala 3.
These kinds of little edge cases can become a blocker when someone is trying to (quickly) move a codebase from Scala 2 to Scala 3.
Indeed, and this observation was exactly the reason I made de post. To find out which of the ones below holds true:
Me being stupid
New undocumented behaviour
A bug in Scala 2: it should have always worked, but was never noticed.
A bug in Scala 3: it should not work, but suddenly it does.
The second turns out to be the case.
Calling this “old style” is based on footnote 6 on page 83 of " Programming in Scala, Fifth Edition" stating “Note, prior to Scala 3, you accessed the elements of a tuple using one-based field names, such as _1 or _2.”
No, i did not. I knew it failed for Scala 2 so that is why i was surprised it suddenly did. (I noticed it while refactoring a tuple to a case class)
… huh, that’s interesting phrasing, and I don’t think of it as having changed in normal cases. Is it referring to the fact that you can now do other things with tuples of more than 22 elements, or has something else changed?
I believe that note is in contrast to Scala 3’s 0-based integer indexing. For example:
scala> val t = ("Hello", 42, true)
val t: (String, Int, Boolean) = (Hello,42,true)
scala> t(0)
val res0: String = Hello
scala> t(1)
val res1: Int = 42
scala> t(2)
val res2: Boolean = true
scala> t(3)
-- Error: ----------------------------------------------------------------------
1 |t(3)
| ^
| Match type reduction failed since selector EmptyTuple.type
| matches none of the cases
|
| case x *: xs => (0 : Int) match {
| case (0 : Int) => x
| case scala.compiletime.ops.int.S[n1] => scala.Tuple.Elem[xs, n1]
| }
1 error found