-
Notifications
You must be signed in to change notification settings - Fork 21
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
case classes and varargs #4919
Comments
Imported From: https://issues.scala-lang.org/browse/SI-4919?orig=1 |
@retronym said (edited on Aug 17, 2011 5:43:58 AM UTC): Maybe there is a place for a lint check on this. |
@paulp said: |
@retronym said (edited on Aug 17, 2011 1:19:20 PM UTC): This reminds me a bit of the lead up to 2.8 when |
@paulp said: |
@adriaanm said: |
@adriaanm said: |
@paulp said: def f(xs: List[Int]): List[Int] = { xs.tail = List(-1, -2, -3) ; xs } would that be a problem? I suggest the answer is "yes, obviously." So what is the difference, other than that list is more widely used? Think of f above as a library function (possibly the standard library) and the user of that library function as someone with a passing concern in security and data integrity. (Only a passing concern, not a serious concern, which is in any case de facto excluded for reasons beyond this one.) |
@adriaanm said (edited on Feb 19, 2014 12:28:15 AM UTC): If you care about performance use an Array, not varargs. Not sure it matters much, but varargs are the work horse of conveniently creating collections. As usual, java interop suffers for it... Lets revisit in 2.12. (See also: scala/scala#3489) |
@paulp said: scala> case class Immutable(xs: Int*)
defined class Immutable
scala> val xs = scala.collection.mutable.ListBuffer(1, 2, 3)
xs: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
scala> def f(xs: Seq[Int]) = Immutable(xs: _*)
f: (xs: Seq[Int])Immutable
scala> val x = f(xs)
x: Immutable = Immutable(ListBuffer(1, 2, 3))
scala> xs ++= 1 to 10
res1: xs.type = ListBuffer(1, 2, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> x
res2: Immutable = Immutable(ListBuffer(1, 2, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) |
@Ichoran said: That is, in Rust, mutability is a property of the context. If you are in a mutable context, you can mutate things. If you are in an immutable context, you can't. If you stick a mutable thing into an immutable context, you can't mutate it any more. (Conversely: if you stick an immutable thing into a mutable context, you can mutate it.) There are both advantages and disadvantages to this approach. It lowers surprise when you wield the system intentionally, but it makes it difficult for you to really protect something from mutation. In Scala as it stands, mutability is a property of the field and therefore you get cases where immutable fields hold mutable objects and vice versa. This is just one example of this sort. Option(Array(1,2,3)) is another. I do think it's bad that X* doesn't have a very clear type, so you can be thinking "immutable" and yet get a mutating version handed to you. Personally I would just disallow X* in case classes, or think of some more robust fix. If you want a vararg in your case class to make it seem like the case class can take an arbitrary number of parameters, you'll be have a bit of a rough ride: scala> case class C(a: Int, xs: Int*)
defined class C
scala> C(1,2,3) // toString doesn't match what we type
res0: C = C(1,WrappedArray(2, 3))
scala> C(1, List(2,3): _*) // Wrapped type leaks through
res1: C = C(1,List(2, 3))
scala> res0 match { case C(a,b,c) => 1 + 2 + 3; case _ => 0 } // Can match 3 args!
res2: Int = 6
scala> res0.productArity // Wait, what?
res3: Int = 2 |
@paulp said: Varargs is built into the language syntax and there is no other way to do what it does. Things which can be described that way merit fixing. |
@Ichoran said: |
@paulp said: |
@Ichoran said: scala> case class C(a: Int, b: Int, c: Int)
defined class C
scala> C(1,2,3).copy(4,5)
res0: C = C(4,5,3)
scala> case class D(a: Int, xs: Int*)
defined class D
scala> D(1,2,3).copy(4,5)
<console>:10: error: value copy is not a member of D
D(1,2,3).copy(4,5) If this is something with a natural fix, then great. But if it doesn't have a natural fix, maybe the privileged position of varargs in case classes is less of a win than we think. |
@Ichoran said (edited on Feb 20, 2014 1:19:40 AM UTC): scala> case class D(a: Int, bs: Int*) {
| def copy(a: Int = this.a): D = new D(a, bs: _*)
| def copy(a: Int, bs: Int*): D = new D(
| a,
| (if (bs.length < this.bs.length) bs ++ this.bs.drop(bs.length) else bs): _*
| )
| }
defined class D
scala> D(1,2,3)
res11: D = D(1,WrappedArray(2, 3))
scala> res11.copy()
res12: D = D(1,WrappedArray(2, 3))
scala> res11.copy(4)
res13: D = D(4,WrappedArray(2, 3))
scala> res11.copy(4,5)
res14: D = D(4,ArrayBuffer(5, 3))
scala> res11.copy(4,5,6,7)
res15: D = D(4,WrappedArray(5, 6, 7)) |
@paulp said: |
@Ichoran said: I agree that copy should be a macro, and one for an inlined extension method (effectively--just insert all the code at the call site). Having it as a method doesn't save any work over having it all at the call-site anyway, and requiring the type at the call site is fine since case classes are typically intended to be leaves in the inheritance hierarchy. I'm still not entirely sure that the copy semantics can be coherent with varargs semantics, but I'm less doubtful than before. |
@SethTisue said: |
related to #11040 |
This issue is fixed by the fact that
|
This seems an unfortunate property for an immutable object.
The text was updated successfully, but these errors were encountered: