-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x @ _*
bound variables are now scala.Seq
; name-based pattern matching changes to enable this
#7068
Conversation
@szeiger this should be ready, I didn't run the full test suite locally though. |
tentatively marked as "prio:blocker"... at least, deciding whether it goes in is a blocker |
00104ff
to
45abc69
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a sensible way out of this corner.
cc @odersky in case you'd like to share your opinion on this |
private def genTake(binder: Symbol, n: Int): List[Tree] = (0 until n).toList map (codegen index seqTree(binder)) | ||
private def genDrop(binder: Symbol, n: Int): List[Tree] = codegen.drop(seqTree(binder))(expectedLength) :: Nil | ||
private def productElemsToN(binder: Symbol, n: Int): List[Tree] = if (n == 0) Nil else 1 to n map tupleSel(binder) toList | ||
private def genTake(binder: Symbol, n: Int): List[Tree] = if (n == 0) Nil else (0 until n).toList map (codegen index seqTree(binder, forceImmutable = false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems we could avoid creating both a Range + a List + another List with List.tabulate(n)(codegen index seqTree(binder, forceImmutable = false))
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, pushed that suggestion.
With name-based pattern matching, unapplySeq can return any type that has an `isEmpty` and `get` methods. The object returned by `get` needs to have `apply`, `length` or `lengthCompare` and `drop` methods. This PR changes the type of `x @ _*` bound to `scala.Seq`. To support that change, the object returned by `unapplySeq.get` is converted by calling `.toSeq` or `drop(n)`. This means there are two changes in the interface for name-based pattern matching: - the object needs to define a `toSeq` method - the `drop` method needs to return a `scala.Seq` The `unapplySeq` method defined in `collection.SeqFactory` now returns a value class wrapper that delegates to the collection. `toSeq` no longer exposes mutable collections. `Array.unapplySeq` uses a similar value class wrapper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
squashed. I'll merge as soon as CI likes it
toSeq
on wildcard-star patternsx @ _*
bound variables are now scala.Seq
; name-based pattern matching changes to enable this
I'm looking at supporting this in Dotty. I have a little question regarding the specification for name-based unapplySeq. Given the following result type from class Data {
def lengthCompare(len: Int): Int
def apply(i: Int): T1 = a(i)
def drop(n: Int): scala.Seq[T2]
def toSeq: scala.Seq[T3]
} My question: does the specification imposes that Ping @lrytz |
This looks like a bug in object Array2 {
def unapplySeq[T](x: Array[T]): UnapplySeqWrapper[T] = new UnapplySeqWrapper(x)
class UnapplySeqWrapper[T](a: Array[T]) {
def isEmpty: Boolean = false
def get: UnapplySeqWrapper[T] = this
def lengthCompare(len: Int): Int = ???
def apply(i: Int): Int = ???
def drop(n: Int): scala.Seq[String] = ???
def toSeq: scala.Seq[Double] = ???
}
}
class Test {
import Array2._
def test(xs: Array[Int]) = xs match {
case Array2(1, 2, ys @ _*) => ys
}
}
|
Another possibility is not to require T1, T2, T3 to be the same, but designate one of them to be the element type, and the other two types should conform to the element type. The motivation for this spec is that it seems to simplify implementation scala/scala3#5078 . |
Do we need a special relationship between these types at all? The shape of the pattern determines statically which methods are called and therefore which one of |
The implementation as of this PR takes the apply method's return type I'm happy to discuss other / better ways to skin that cat, of course. |
I'd like to point out that the scalac error message says: "error during expansion of this match (this is a scalac bug)" So if it is expected, I would not report it as a scalac bug 😄 |
Yes, you're right about the message being confusing :-) See scala/bug#11102 |
Thanks a lot @lrytz , making the result type of |
@szeiger It matters for Dotty (I'm not sure about Scalac) as semantic translation of patmat happens after type checking. The typer just assumes an element type. If the types |
With name-based pattern matching, unapplySeq can return any type that
has an
isEmpty
andget
methods. The object returned byget
needsto have
apply
,length
orlengthCompare
anddrop
methods.This PR changes the type of
x @ _*
bound variables toscala.Seq
. To supportthat change, the object returned by
unapplySeq.get
is converted bycalling
.toSeq
ordrop(n)
.This means there are two changes in the interface for name-based pattern
matching:
toSeq
methoddrop
method needs to return ascala.Seq
The
unapplySeq
method defined incollection.SeqFactory
now returnsa value class wrapper that delegates to the collection.
toSeq
nolonger exposes mutable collections.
Array.unapplySeq
uses a similar value class wrapper.Fixes scala/bug#11040