This page is no longer maintained — Please continue to the home page at www.scala-lang.org

case class with varargs not immutable

26 replies
wookietreiber
Joined: 2011-04-24,
User offline. Last seen 42 years 45 weeks ago.

hi

I just discovered something I wasn't expecting:

-------------------bad because no immutability---------------------
scala> case class Foo(f: Int*)
defined class Foo

scala> val f = Foo(2,2,6)
f: Foo = Foo(WrappedArray(2, 2, 6))

scala> val a = f.f
a: Int* = WrappedArray(2, 2, 6)

scala> a.getClass
res0: java.lang.Class[_] = class scala.collection.mutable.WrappedArray$ofInt
-------------------------------------------------------------------

shouldn't that be some kind of immutable Seq? ... the WrappedArray makes
the case class mutable, which is actually what we don't want especially if the
case class should be used by actors !

Or is there some hidden way of using varargs case classes that are in fact
immutable?

... the advantage of using varargs is additional code reduction compared to:

----bad because one has to write List(...) @ constructor/apply-----
scala> case class Bar(b: List[Int])
defined class Bar

scala> val b = Bar(List(2,2,6))
b: Bar = Bar(List(2, 2, 6))
-------------------------------------------------------------------

or

--------bad because one has to write two additional defs-----------
scala> case class FooBar(b: List[Int]) { def this(ints: Int*) = this(ints.toList) } ; object FooBar { def apply(ints: Int*) = new FooBar(ints.toList) }
defined class FooBar
defined module FooBar

scala> FooBar(2,2,6)
res2: FooBar = FooBar(List(2, 2, 6))
-------------------------------------------------------------------

have I missed some doc that addresses this issue? if not, there should
definetely be some kind of doc on this topic because of the possible
immutability screw-up ...

best regards
wookietreiber

Garrett Rowe 2
Joined: 2009-03-23,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

Have you tried mutating the underlying array? Were you able to do it?
(I tried and I wasn't)

Adam Rabung
Joined: 2010-04-23,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable
case classes just assure you the fields are vals (non-reassignable), not that the they're immutable.

scala> import scala.collection.mutable.ListBufferimport scala.collection.mutable.ListBuffer
scala> case class Foo(f:ListBuffer[Int])defined class Foo
scala> val f1 = new Foo(ListBuffer(1,2,3) )f1: Foo = Foo(ListBuffer(1, 2, 3))
scala> f1.f(2) = 55
scala> f1res22: Foo = Foo(ListBuffer(1, 2, 55))
scala> f1.f = ListBuffer(4,5,6)<console>:16: error: reassignment to val        f1.f = ListBuffer(4,5,6)
On Tue, Aug 16, 2011 at 3:20 PM, wookietreiber <kizkizzbangbang@googlemail.com> wrote:
hi

I just discovered something I wasn't expecting:


-------------------bad because no immutability---------------------
scala> case class Foo(f: Int*)
defined class Foo

scala> val f = Foo(2,2,6)
f: Foo = Foo(WrappedArray(2, 2, 6))

scala> val a = f.f
a: Int* = WrappedArray(2, 2, 6)

scala> a.getClass
res0: java.lang.Class[_] = class scala.collection.mutable.WrappedArray$ofInt
-------------------------------------------------------------------


shouldn't that be some kind of immutable Seq? ... the WrappedArray makes
the case class mutable, which is actually what we don't want especially if the
case class should be used by actors !

Or is there some hidden way of using varargs case classes that are in fact
immutable?

... the advantage of using varargs is additional code reduction compared to:


----bad because one has to write List(...) @ constructor/apply-----
scala> case class Bar(b: List[Int])
defined class Bar

scala> val b = Bar(List(2,2,6))
b: Bar = Bar(List(2, 2, 6))
-------------------------------------------------------------------

or

--------bad because one has to write two additional defs-----------
scala> case class FooBar(b: List[Int]) { def this(ints: Int*) = this(ints.toList) } ; object FooBar { def apply(ints: Int*) = new FooBar(ints.toList) }
defined class FooBar
defined module FooBar

scala> FooBar(2,2,6)
res2: FooBar = FooBar(List(2, 2, 6))
-------------------------------------------------------------------


have I missed some doc that addresses this issue? if not, there should
definetely be some kind of doc on this topic because of the possible
immutability screw-up ...

best regards
wookietreiber


Lex
Joined: 2010-02-28,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

On Tue, Aug 16, 2011 at 3:00 PM, Adam Rabung wrote:
> case classes just assure you the fields are vals (non-reassignable), not
> that the they're immutable.

Arguable a case class with varargs is a corner case, because arguments
listed in the constructor can be changed even if values themselves are
immutable.

Garrett Rowe 2
Joined: 2009-03-23,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

scala> case class C(n: Int*)
defined class C

scala> val c = C(1, 2, 3)
c: C = C(WrappedArray(1, 2, 3))

scala> c.n
res0: Int* = WrappedArray(1, 2, 3)

scala> c.n(1)
res1: Int = 2

scala> c.n(1) = 1
:11: error: value update is not a member of Int*
c.n(1) = 1
^

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable


On Tue, Aug 16, 2011 at 4:18 PM, Garrett Rowe <gmrowe1975@gmail.com> wrote:
scala> case class C(n: Int*)
defined class C

scala> val c = C(1, 2, 3)
c: C = C(WrappedArray(1, 2, 3))

scala> c.n
res0: Int* = WrappedArray(1, 2, 3)

scala> c.n(1)
res1: Int = 2

scala> c.n(1) = 1
<console>:11: error: value update is not a member of Int*
      c.n(1) = 1
        ^

Okay, try this: c.n match {  case wa: scala.collection.mutable.WrappedArray[Int] => wa(1) = 1}

andreak 2
Joined: 2011-07-21,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable
On 08/16/2011 10:27 PM, Naftoli Gugenheim wrote:
CANpg8PAQLOWgwHRjB2uvbfHPbPWUs+Qy-0Dt7cSOMM6Q+TKVRQ [at] mail [dot] gmail [dot] com" type="cite">

On Tue, Aug 16, 2011 at 4:18 PM, Garrett Rowe <gmrowe1975 [at] gmail [dot] com" rel="nofollow">gmrowe1975@gmail.com> wrote:
scala> case class C(n: Int*)
defined class C

scala> val c = C(1, 2, 3)
c: C = C(WrappedArray(1, 2, 3))

scala> c.n
res0: Int* = WrappedArray(1, 2, 3)

scala> c.n(1)
res1: Int = 2

scala> c.n(1) = 1
<console>:11: error: value update is not a member of Int*
      c.n(1) = 1
        ^

Okay, try this:  c.n match {   case wa: scala.collection.mutable.WrappedArray[Int] => wa(1) = 1 }

Or this:

scala> case class C(n: Int*)
defined class C

scala> val c = C(1, 2, 3)
c: C = C(WrappedArray(1, 2, 3))

scala> c.n.asInstanceOf[scala.collection.mutable.WrappedArray[Int]](1) = 44

scala> c.n(1)
res1: Int = 44
-- 
Andreas Joseph Krogh 
Senior Software Developer / CTO
Public key: http://home.officenet.no/~andreak/public_key.asc
------------------------+---------------------------------------------+
OfficeNet AS            | The most difficult thing in the world is to |
Rosenholmveien 25       | know how to do a thing and to watch         |
1414 Trollåsen          | somebody else doing it wrong, without       |
NORWAY                  | comment.                                    |
Org.nr: NO 981 479 076  |                                             |
                        |                                             |
Tlf:    +47 24 15 38 90 |                                             |
Fax:    +47 24 15 38 91 |                                             |
Mobile: +47 909  56 963 |                                             |
------------------------+---------------------------------------------+
dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: case class with varargs not immutable

On Tue, Aug 16, 2011 at 17:49, Andreas Joseph Krogh
wrote:
> On 08/16/2011 10:27 PM, Naftoli Gugenheim wrote:
>
> On Tue, Aug 16, 2011 at 4:18 PM, Garrett Rowe wrote:
>>
>> scala> case class C(n: Int*)
>> defined class C
>>
>> scala> val c = C(1, 2, 3)
>> c: C = C(WrappedArray(1, 2, 3))
>>
>> scala> c.n
>> res0: Int* = WrappedArray(1, 2, 3)
>>
>> scala> c.n(1)
>> res1: Int = 2
>>
>> scala> c.n(1) = 1
>> :11: error: value update is not a member of Int*
>>       c.n(1) = 1
>>         ^
>
> Okay, try this:
>  c.n match {
>   case wa: scala.collection.mutable.WrappedArray[Int] => wa(1) = 1
> }
>
> Or this:
>
> scala> case class C(n: Int*)
> defined class C
>
> scala> val c = C(1, 2, 3)
> c: C = C(WrappedArray(1, 2, 3))
>
> scala> c.n.asInstanceOf[scala.collection.mutable.WrappedArray[Int]](1) = 44
>
> scala> c.n(1)
> res1: Int = 44

By the same token, I can change the value of a "final val" using
reflection. Scala doesn't guard against people going out of their way
to mutate things that they shouldn't.

Alex Cruise
Joined: 2008-12-17,
User offline. Last seen 2 years 26 weeks ago.
Re: case class with varargs not immutable
On Tue, Aug 16, 2011 at 1:00 PM, Adam Rabung <adamrabung@gmail.com> wrote:
case classes just assure you the fields are vals (non-reassignable), not that the they're immutable.

[...] 
 
scala> val f1 = new Foo(ListBuffer(1,2,3) )f1: Foo = Foo(ListBuffer(1, 2, 3))
scala> f1.f(2) = 55

It's interesting to consider whether the generated constructor should be doing a defensive copy of a varargs parameter if it turns out the value provided is mutable...
-0xe1a
wookietreiber
Joined: 2011-04-24,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

On Tue, Aug 16, 2011 at 03:09:02PM -0500, Aleksey Nikiforov wrote:
> On Tue, Aug 16, 2011 at 3:00 PM, Adam Rabung wrote:
> > case classes just assure you the fields are vals (non-reassignable), not
> > that the they're immutable.
>
> Arguable a case class with varargs is a corner case, because arguments
> listed in the constructor can be changed even if values themselves are
> immutable.

what I'd really like is if case classes would turn the WrappedArray
automatically into an immutable collection, don't know though whether a
LinearSeq or an IndexedSeq ... but this corner case is IMHO worth a
different varargs behaviour.

best regards
wookietreiber

andreak 2
Joined: 2011-07-21,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

On 08/17/2011 12:48 AM, Daniel Sobral wrote:
> On Tue, Aug 16, 2011 at 17:49, Andreas Joseph Krogh
> wrote:
>> On 08/16/2011 10:27 PM, Naftoli Gugenheim wrote:
>>
>> On Tue, Aug 16, 2011 at 4:18 PM, Garrett Rowe wrote:
>>> scala> case class C(n: Int*)
>>> defined class C
>>>
>>> scala> val c = C(1, 2, 3)
>>> c: C = C(WrappedArray(1, 2, 3))
>>>
>>> scala> c.n
>>> res0: Int* = WrappedArray(1, 2, 3)
>>>
>>> scala> c.n(1)
>>> res1: Int = 2
>>>
>>> scala> c.n(1) = 1
>>> :11: error: value update is not a member of Int*
>>> � � � c.n(1) = 1
>>> � � � � ^
>> Okay, try this:
>> �c.n match {
>> � case wa: scala.collection.mutable.WrappedArray[Int] => wa(1) = 1
>> }
>>
>> Or this:
>>
>> scala> case class C(n: Int*)
>> defined class C
>>
>> scala> val c = C(1, 2, 3)
>> c: C = C(WrappedArray(1, 2, 3))
>>
>> scala> c.n.asInstanceOf[scala.collection.mutable.WrappedArray[Int]](1) = 44
>>
>> scala> c.n(1)
>> res1: Int = 44
> By the same token, I can change the value of a "final val" using
> reflection. Scala doesn't guard against people going out of their way
> to mutate things that they shouldn't.

I would say that's another issue. The examples above don't use any fancy
tricks, just cast to the the runtime-type, which in this case differs
from the declared type. All of this is known to the compiler. The
Scala-compiler implements vals, singletons(object) etc. and as long as
you don't use reflection or by any other means go behind the compilers
back things work as expected.

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable


On 17 August 2011 00:11, Alex Cruise <alex@cluonflux.com> wrote:

It's interesting to consider whether the generated constructor should be doing a defensive copy of a varargs parameter if it turns out the value provided is mutable...
-0xe1a

Defensive copies are Bad Things. They have a tendency to burn CPU and GC. If you really think that Foo* should be immutable, then we could introduce the (breaking) change that Foo* must be a collection.immutable implementation of Seq.
Matthew

--
Dr Matthew PocockVisitor, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozertel: (0191) 2566550mob: +447535664143
Aydjen
Joined: 2009-08-21,
User offline. Last seen 1 year 28 weeks ago.
Re: case class with varargs not immutable

Hi,

 

let's try this without casting or pattern matching:

 

scala> val a = Array(1, 2, 3, 4, 5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> case class Foo(n: Int*)
defined class Foo

scala> val b = Foo(a:_*)
b: Foo = Foo(WrappedArray(1, 2, 3, 4, 5))

scala> a(0) = 42

scala> b
res1: Foo = Foo(WrappedArray(42, 2, 3, 4, 5))

 

To be honest, this surprised me. But still I think the behaviour is OK because it's my fault I used an array and mutated things in the first place.  

 

Kind regards

Andreas 


Daniel Sobral <dcsobral@gmail.com> 2011/8/17 00:48:

> By the same token, I can change the value of a "final val" using
> reflection. Scala doesn't guard against people going out of their way
> to mutate things that they shouldn't.

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

On 17 August 2011 13:39, Andreas Flierl <andreas@flierl.eu> wrote:

Hi,

 

let's try this without casting or pattern matching:

 

scala> val a = Array(1, 2, 3, 4, 5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> case class Foo(n: Int*)
defined class Foo

scala> val b = Foo(a:_*)
b: Foo = Foo(WrappedArray(1, 2, 3, 4, 5))

scala> a(0) = 42


For me, this case is a far more compelling argument that Foo* should desugar to something from collections.immutable. There really should be no way to mutate the input varargs at all. a(0) = 42 should not compile.
 

scala> b
res1: Foo = Foo(WrappedArray(42, 2, 3, 4, 5))

 

To be honest, this surprised me. But still I think the behaviour is OK because it's my fault I used an array and mutated things in the first place.  


I personally don't think this case is OK at all, because the person calling the method and the person who implemented the method may be different people, and may have little or no knowledge of the details of how the argument is used outside and inside the method call. When you say it's your fault, it's your fault for not being all-knowing - never a very sound requirement. The type system is there to, where practical, prevent you writing code that breaks.
Matthew
 

 

Kind regards

Andreas 


Daniel Sobral <dcsobral@gmail.com> 2011/8/17 00:48:

> By the same token, I can change the value of a "final val" using
> reflection. Scala doesn't guard against people going out of their way
> to mutate things that they shouldn't.




--
Dr Matthew PocockVisitor, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozertel: (0191) 2566550mob: +447535664143
dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: case class with varargs not immutable

On Wed, Aug 17, 2011 at 04:44, Andreas Joseph Krogh
wrote:
>>> scala> c.n.asInstanceOf[scala.collection.mutable.WrappedArray[Int]](1) = 44
>>>
>>> scala> c.n(1)
>>> res1: Int = 44
>> By the same token, I can change the value of a "final val" using
>> reflection. Scala doesn't guard against people going out of their way
>> to mutate things that they shouldn't.
>
> I would say that's another issue. The examples above don't use any fancy
> tricks, just cast to the the runtime-type, which in this case differs
> from the declared type. All of this is known to the compiler. The

To "cast" means "I'm ditching type safety now". It's not a fancy
trick, it is just you telling the compiler you don't care about type
safety, so how can you then complain about lack of mutability?

You know what else have mutability underneath? List and Vector.
Granted, it is more difficult to get at it than to get at that
WrappedArray, but, in both cases, you have to *work* at it. You have
to explicitly get a collection that, statically, is non-mutable, and
do some dancing to get the mutable meat of it.

That going through reflection takes longer is just a statement on its
API design and the fact that Scala lacks its own reflection library.
Reflection is no more "fancy" that "asInstanceOf", and, arguably, the
latter is much more dangerous.

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: case class with varargs not immutable

On Wed, Aug 17, 2011 at 10:00, Matthew Pocock
wrote:
>
> On 17 August 2011 13:39, Andreas Flierl wrote:
>>
>> Hi,
>>
>>
>>
>> let's try this without casting or pattern matching:
>>
>>
>>
>> scala> val a = Array(1, 2, 3, 4, 5)
>> a: Array[Int] = Array(1, 2, 3, 4, 5)
>>
>> scala> case class Foo(n: Int*)
>> defined class Foo
>>
>> scala> val b = Foo(a:_*)
>> b: Foo = Foo(WrappedArray(1, 2, 3, 4, 5))
>>
>> scala> a(0) = 42
>>
>
> For me, this case is a far more compelling argument that Foo* should desugar
> to something from collections.immutable. There really should be no way to
> mutate the input varargs at all. a(0) = 42 should not compile.

Read again, "a" is not a vararg, it is an Array. What is a vararg is
"n", the constructor parameter of b.

I even thought this was against spec, but the spec says that, when you
use "e :_*", then Scala passes "e" as the parameter.

>
>>
>> scala> b
>> res1: Foo = Foo(WrappedArray(42, 2, 3, 4, 5))
>>
>>
>>
>> To be honest, this surprised me. But still I think the behaviour is OK
>> because it's my fault I used an array and mutated things in the first place.
>>
>
> I personally don't think this case is OK at all, because the person calling
> the method and the person who implemented the method may be different
> people, and may have little or no knowledge of the details of how the
> argument is used outside and inside the method call. When you say it's your
> fault, it's your fault for not being all-knowing - never a very sound
> requirement. The type system is there to, where practical, prevent you
> writing code that breaks.
> Matthew
>
>>
>>
>>
>> Kind regards
>>
>> Andreas
>>
>> Daniel Sobral 2011/8/17 00:48:
>>
>> > By the same token, I can change the value of a "final val" using
>> > reflection. Scala doesn't guard against people going out of their way
>> > to mutate things that they shouldn't.
>>
>
>
>
> --
> Dr Matthew Pocock
> Visitor, School of Computing Science, Newcastle University
> mailto: turingatemyhamster@gmail.com
> gchat: turingatemyhamster@gmail.com
> msn: matthew_pocock@yahoo.co.uk
> irc.freenode.net: drdozer
> tel: (0191) 2566550
> mob: +447535664143
>

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: case class with varargs not immutable
On Wed, Aug 17, 2011 at 9:00 AM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:

On 17 August 2011 13:39, Andreas Flierl <andreas@flierl.eu> wrote:

Hi,

 

let's try this without casting or pattern matching:

 

scala> val a = Array(1, 2, 3, 4, 5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> case class Foo(n: Int*)
defined class Foo

scala> val b = Foo(a:_*)
b: Foo = Foo(WrappedArray(1, 2, 3, 4, 5))

scala> a(0) = 42


For me, this case is a far more compelling argument that Foo* should desugar to something from collections.immutable. There really should be no way to mutate the input varargs at all. a(0) = 42 should not compile.

Huh?  val a = Array(1); a(0) = 2 shouldn't compile?!

I think you're just interpreting x: _* incorrectly.  If you think of it as:
  "f(x: _*) means I am using x as the container for my varargs"
then there's no problem.  Mutable container in?  Well, you can still mutate it.  Immutable in?  Can't.

If you think of it as:
  "f(x: _*) is a shorthand for f(x(0),x(1),x(2),...)"
then you become deeply worried that when you modify x, you also modify what you passed to f.

So, think about it the former way.  If the documentation does not currently make this clear, perhaps that could be improved.

Making defensive copies is inefficient.  For those people who need efficiency, forcing inefficiency is a bad idea.  For those people who need the improved correctness of immutability, _what were they doing with an Array to begin with_?!

  --Rex

Aydjen
Joined: 2009-08-21,
User offline. Last seen 1 year 28 weeks ago.
Re: case class with varargs not immutable

Hi,

exactly my thoughts, that's why I wrote "my fault because I was using and mutating an array in the first place".

 

Kind regards

Andreas 

 


Rex Kerr <ichoran@gmail.com> 2011/8/17 17:28:

For those people who need the improved correctness of immutability, _what were they doing with an Array to begin with_?!
wookietreiber
Joined: 2011-04-24,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

what I originally had in mind was not using arrays with case classes
but varargs with case classes, which, unfortunately, are turned into
an array

best regards
wookietreiber

On Wed, Aug 17, 2011 at 05:40:13PM +0200, Andreas Flierl wrote:
> Hi,
>
> exactly my thoughts, that's why I wrote "my fault because I was using and
> mutating an array in the first place".
>
>
>
> Kind regards
>
> Andreas
>
>
>
>
> Rex Kerr 2011/8/17 17:28:
>
>
> For those people who need the improved correctness of immutability, _what
> were they doing with an Array to begin with_?!
>

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable


On 17 August 2011 16:40, Andreas Flierl <andreas@flierl.eu> wrote:

Hi,

exactly my thoughts, that's why I wrote "my fault because I was using and mutating an array in the first place".


I still believe that it would be safer if the Foo* notation desugared to a Seq from collections.immutable. However, perhaps someone can provide a genuine use-case for a function that takes varargs and mutates them, or a case class constructor that accepts a varargs and expects them to be mutated. I don't have the same reservation about signatures that explicitly mention a collection type - it's the Foo* case that I'm talking about.
Matthew 

 

Kind regards

Andreas 

 


Rex Kerr <ichoran@gmail.com> 2011/8/17 17:28:

For those people who need the improved correctness of immutability, _what were they doing with an Array to begin with_?!



--
Dr Matthew PocockVisitor, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozertel: (0191) 2566550mob: +447535664143
ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: case class with varargs not immutable
On Wed, Aug 17, 2011 at 1:40 PM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:

On 17 August 2011 16:40, Andreas Flierl <andreas@flierl.eu> wrote:

Hi,

exactly my thoughts, that's why I wrote "my fault because I was using and mutating an array in the first place".


I still believe that it would be safer if the Foo* notation desugared to a Seq from collections.immutable. However, perhaps someone can provide a genuine use-case for a function that takes varargs and mutates them, or a case class constructor that accepts a varargs and expects them to be mutated. I don't have the same reservation about signatures that explicitly mention a collection type - it's the Foo* case that I'm talking about.
Matthew 

It's typed as a scala.collection.Seq.  Look:

scala> def mf[A: Manifest](a: A) = implicitly[Manifest[A]]
mf: [A](a: A)(implicit evidence$1: Manifest[A])Manifest[A]

scala> def varg(xs: Int*) = xs
varg: (xs: Int*)Int*

scala> mf(varg(1,2,3))
res1: Manifest[Seq[Int]] = scala.collection.Seq[Int]

scala> varg(1,2,3).update(0,5)
<console>:26: error: value update is not a member of Int*
       varg(1,2,3).update(0,5)

scala.collection.Seq doesn't have any self-mutating methods.  So you won't have any problems unless you think that Int* means "make a copy" when it really just means Seq[Int].  The function taking varargs can't change the contents unless it resorts to casting (which it cannot do blindly, because you very well could have passed in a List instead of a WrappedArray).  And presumably if you have a mutable collection that you want to mutate, and the method might hold on to the collection (e.g. because it's a constructor), then you are always free to make a copy defensively.

I just don't see how the normal use cases are problematic.

  --Rex

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable

On 17 August 2011 18:53, Rex Kerr <ichoran@gmail.com> wrote:
 

It's typed as a scala.collection.Seq. 

I know this. If it where typed Seq with Immutable, or something similar, these corner cases would not have been possible and people would not have been surprised. This doesn't rule out people declaring methods as taking Seq[Foo] and leveraging mutability. It only applies to the Foo* case.  
scala.collection.Seq doesn't have any self-mutating methods.

No, but some implementations do. All the mutability exploits described in this thread make use of mutable sub-classes. Desugaring Foo* to Seq[Foo] with Immutable would have ruled all of these out on type-safety grounds.  
 The function taking varargs can't change the contents unless it resorts to casting (which it cannot do blindly, because you very well could have passed in a List instead of a WrappedArray). 

I agree. This is why I see the case of passing in a pre-existing mutable collection where a varargs is expected as the more serious case. 
And presumably if you have a mutable collection that you want to mutate, and the method might hold on to the collection (e.g. because it's a constructor), then you are always free to make a copy defensively.

If you realise the implications of passing a mutable collection in, then yes. I think it's already been established in this thread that at least some people did not realise this implication and found this behaviour surprising until it had been explained or they had worked it out. Having Foo* desugar to Seq[Foo] with Immutable would have prevented this entirely, with no practical decrease in the signatures and data-types that we can represent in scala. Varargs are syntactic sugar, and as such have a higher bar for not causing surprise to the naive user.
I'd love to know how common it is to pass mutable Seq impls as varargs parameters when using the foo : _* notation. Is there a body of existing scala code that we could grep through for this?
Matthew 
  --Rex




--
Dr Matthew PocockVisitor, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozertel: (0191) 2566550mob: +447535664143
Jason Zaugg
Joined: 2009-05-18,
User offline. Last seen 38 weeks 5 days ago.
Re: case class with varargs not immutable

On Wed, Aug 17, 2011 at 10:11 PM, Matthew Pocock
wrote:
> I'd love to know how common it is to pass mutable Seq impls as varargs
> parameters when using the foo : _* notation. Is there a body of existing
> scala code that we could grep through for this?

There are 258k lines here [1]. The even claim to compile in congregation.

-jason

[1] https://github.com/alacscala/scala-corpus

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable
Hi,
I've been working through this. I can find 745 cases of the foo: _* construct in the corpus. I've been marking them up with things like:
fwd: A vararg parameter is passed into another def as _*. java (array): An Array[Foo] is passed into a Java varargs method that expects an arrayarray (java): An Array[Foo] returned from a Java method<type>: The foo is of this type - i.Foo for immutable, m.Foo for mutable - In some cases I'm inferring the type <Foo> op: Some operation on a Foo collection where I can't by eye infer the type e.g. map, filter, sortBy, ... on Seq
I've used a few other notations here and there.
After the first 100 examples, I got bored. However, the stats so far are:
fwd: 53i.List: 27java (array): 13Seq ???: 2?.toArray: 1i.Range: 1 array (java): 1
So, at least in the first 100 things I saw, there would be minimal impact for making Foo* desugar to Seq[Foo] with Immutable, with a special-case for java arrays to handle the Java interop case. Of course, these numbers are likely to be a bit skewed, because they are library and library test code, not user-code.
Matthew
On 17 August 2011 21:48, Jason Zaugg <jzaugg@gmail.com> wrote:
On Wed, Aug 17, 2011 at 10:11 PM, Matthew Pocock
<turingatemyhamster@gmail.com> wrote:
> I'd love to know how common it is to pass mutable Seq impls as varargs
> parameters when using the foo : _* notation. Is there a body of existing
> scala code that we could grep through for this?

There are 258k lines here [1]. The even claim to compile in congregation.

-jason

[1] https://github.com/alacscala/scala-corpus



--
Dr Matthew PocockVisitor, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozertel: (0191) 2566550mob: +447535664143
ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: case class with varargs not immutable
On Thu, Aug 18, 2011 at 12:09 PM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:
After the first 100 examples, I got bored. However, the stats so far are:
fwd: 53i.List: 27java (array): 13Seq ???: 2?.toArray: 1i.Range: 1 array (java): 1
So, at least in the first 100 things I saw, there would be minimal impact for making Foo* desugar to Seq[Foo] with Immutable, with a special-case for java arrays to handle the Java interop case.

Thanks for the data!  This is interesting.  But I don't see how you get to your conclusion of minimal impact.  ~50% of the code is forwarded; of the non forwarded section, about 1/3 needs to be arrays for Java interop.

You can't special case directly at the Java call, since it may well be a forward, and you want an underlying array.  So you have to either take a huge performance hit, or somehow wrap arrays in a different collection than WrappedArray (just so that the user can't cast their way into trouble).

  --Rex

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: case class with varargs not immutable


On 18 August 2011 17:19, Rex Kerr <ichoran@gmail.com> wrote:

Thanks for the data!  This is interesting.  But I don't see how you get to your conclusion of minimal impact.  ~50% of the code is forwarded; of the non forwarded section, about 1/3 needs to be arrays for Java interop.

The single case I'm interested in is where :_* is used to pass a seq into a def using varargs. All forwarded calls are by definition unaffected by any change. Take the trivial case:
def bar(vals: Int*) = ...def foo(vals: Int*) = bar(vals : _*)
Here, making Int* desugar to something other than Seq[Int] has the identical impact on both foo and bar. If the call to bar in foo compiles before the desugaring change, it will also compile after. So, from the point of view of seeing how many uses of the :_* syntax get broken, these are safe uses.
As for java interop, since Java uses arrays for its varargs, we certainly would need to handle this as a special case. I presume that scalac knows when a call is to a java (not generated by scalac) method that accepts an array in the varargs position.
The problem cases are the two where Seq[Foo] is explicitly mentioned. They are more like this:
def bas(vals: Seq[Int]) = bar(vals: _*)
So, the way I'm counting it, these 2 cases are the ones where changing the desugaring of Foo* would make existing code not compile on use of :_* so are the only cases with an impact.
Matthew 

  --Rex




--
Dr Matthew PocockVisitor, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozertel: (0191) 2566550mob: +447535664143
ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: case class with varargs not immutable
On Thu, Aug 18, 2011 at 12:58 PM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:


On 18 August 2011 17:19, Rex Kerr <ichoran@gmail.com> wrote:

Thanks for the data!  This is interesting.  But I don't see how you get to your conclusion of minimal impact.  ~50% of the code is forwarded; of the non forwarded section, about 1/3 needs to be arrays for Java interop.

The single case I'm interested in is where :_* is used to pass a seq into a def using varargs. All forwarded calls are by definition unaffected by any change. Take the trivial case:
def bar(vals: Int*) = ...

Here, let me fill that in for you.

def bar(vals: Int*) = java.util.Arrays.asList(vals: _*)  // I am a special case!
 
def foo(vals: Int*) = bar(vals : _*)

def foo(vals: Int*) = bar(vals: _*)  // Uh-oh, I need to be a special case but I'm not!

So these aren't actually unaffected.  Any special-casing has to be referred all the way through the hierarchy of calls.  That's not easy to do in general.
 

As for java interop, since Java uses arrays for its varargs, we certainly would need to handle this as a special case. I presume that scalac knows when a call is to a java (not generated by scalac) method that accepts an array in the varargs position.

You say that this should be handled as a special case, but you haven't specified how.  The way it's handled now works pretty well: small wrapper around an array which you can create implicitly and get out of implicitly via a toArray method that just returns the underlying array.
 

The problem cases are the two where Seq[Foo] is explicitly mentioned. They are more like this:
def bas(vals: Seq[Int]) = bar(vals: _*)
So, the way I'm counting it, these 2 cases are the ones where changing the desugaring of Foo* would make existing code not compile on use of :_* so are the only cases with an impact.

This would compile with an implicit conversion, but it might perform unacceptably.

  --Rex

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland