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

Re: ListBuffer.++, and List.+

4 replies
Seth Tisue
Joined: 2008-12-16,
User offline. Last seen 34 weeks 3 days ago.

>>>>> "Dimitris" == Dimitris Andreou writes:

Dimitris> I think this should be made to work:
> val x = new ListBuffer[Int]
> x += 1
> x ++ x

Dimitris> Now it throws an OOME.

That seems like a reasonable outcome to me, actually.

Dimitris> Also, when I try this: val x = List(1) x += 2

Dimitris> I get a compiler error "reassignment to val". But there is
Dimitris> no "+=" method defined in list, only "+". Does the compiler
Dimitris> treat "x += y", when there is no x.+= method, as "x = x + y"?

Yes.

ounos
Joined: 2008-12-29,
User offline. Last seen 3 years 44 weeks ago.
Re: ListBuffer.++, and List.+

O/H Seth Tisue έγραψε:
>>>>>> "Dimitris" == Dimitris Andreou writes:
>>>>>>
>
> Dimitris> I think this should be made to work:
> > val x = new ListBuffer[Int]
> > x += 1
> > x ++ x
>
> Dimitris> Now it throws an OOME.
>
> That seems like a reasonable outcome to me, actually.
>
In Java, this works:
List list = new ArrayList();
list.add(1);
list.addAll(list);
//list is [1, 1]

In my view, it's an implementation artifact (although an understandable
one). Many similar problems regarding self-referencing collections
existed in java.util classes, and they were all fixed (for example, a
collections's toString() which contained itself, or
collection.addAll(collection), etc).

Otherwise it should be documented, saying that it can't handle itself as
an argument (many other methods in Scala distribution already document
the conditions under which they may not terminate).

Dimitris

Sean McDirmid
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: ListBuffer.++, and List.+
I think if we documented all dog-chasing-own-tail cases we'd have a lot of documentation that would drown out the useful stuff. 

On Tue, Jan 13, 2009 at 4:06 AM, Dimitris Andreou <jim.andreou@gmail.com> wrote:
O/H Seth Tisue έγραψε:
"Dimitris" == Dimitris Andreou <jim.andreou@gmail.com> writes:
           

 Dimitris> I think this should be made to work:
 > val x = new ListBuffer[Int]
 > x += 1
 > x ++ x

 Dimitris> Now it throws an OOME.

That seems like a reasonable outcome to me, actually.
 
In Java, this works:
List list = new ArrayList();
list.add(1);
list.addAll(list);
//list is [1, 1]

In my view, it's an implementation artifact (although an understandable one). Many similar problems regarding self-referencing collections existed in java.util classes, and they were all fixed (for example, a collections's toString() which contained itself, or collection.addAll(collection), etc).

Otherwise it should be documented, saying that it can't handle itself as an argument (many other methods in Scala distribution already document the conditions under which they may not terminate).

Dimitris

ounos
Joined: 2008-12-29,
User offline. Last seen 3 years 44 weeks ago.
Re: ListBuffer.++, and List.+

O/H Sean McDirmid έγραψε:
> I think if we documented all dog-chasing-own-tail cases we'd have a
> lot of documentation that would drown out the useful stuff.
Then why don't you prefer simply fixing the issue, once and for all?
Here is some code that highlights the problem. A library author offers a
class that returns read-only lists of numbers. And the user can append
more numbers to it. A user tries to double the numbers => kaboom.

class Numbers {
import scala.collection.mutable.ListBuffer
private val list = new ListBuffer[Int]
def numbers: Seq[Int] = list
def append(newNumbers: Seq[Int]): Unit = list ++ newNumbers
}

Why should the author of this have to check "if (newNumbers == list) ...
" and do a custom work-around? Or why would he have to return defensive
copies, even if the returned values are *not* modified outside? Or
should the author document this?

Also I noticed some other similar behaviors in java collection wrappers,
which is puzzling:

scala> import scala.collection.jcl.ArrayList
scala> val x = new ArrayList[AnyRef]
scala> x.add(x)
scala> x //throws StackOverflowError, the original java.util.ArrayList
works fine
scala> x.hashCode //throws StackOverflowError, the original
java.util.ArrayList works fine
//others? who knows

In any case, as Josh Bloch put it, we live in an economy of scale, and
things get written once and used millions of times (well, not millions
in this case but who knows what the future holds :-) ), so even the
slightest effort that makes such a commonly-used library more convenient
to use, pays off.
>
> On Tue, Jan 13, 2009 at 4:06 AM, Dimitris Andreou
> > wrote:
>
> O/H Seth Tisue ������:
>
> "Dimitris" == Dimitris Andreou
> > writes:
>
>
>
> Dimitris> I think this should be made to work:
> > val x = new ListBuffer[Int]
> > x += 1
> > x ++ x
>
> Dimitris> Now it throws an OOME.
>
> That seems like a reasonable outcome to me, actually.
>
>
> In Java, this works:
> List list = new ArrayList();
> list.add(1);
> list.addAll(list);
> //list is [1, 1]
>
> In my view, it's an implementation artifact (although an
> understandable one). Many similar problems regarding
> self-referencing collections existed in java.util classes, and
> they were all fixed (for example, a collections's toString() which
> contained itself, or collection.addAll(collection), etc).
>
> Otherwise it should be documented, saying that it can't handle
> itself as an argument (many other methods in Scala distribution
> already document the conditions under which they may not terminate).
>
> Dimitris
>
>

David Pollak
Joined: 2008-12-16,
User offline. Last seen 42 years 45 weeks ago.
Re: ListBuffer.++, and List.+


On Tue, Jan 13, 2009 at 6:03 AM, Dimitris Andreou <jim.andreou@gmail.com> wrote:
O/H Sean McDirmid έγραψε:
I think if we documented all dog-chasing-own-tail cases we'd have a lot of documentation that would drown out the useful stuff.
Then why don't you prefer simply fixing the issue, once and for all?

While this is a problem from your perspective, it's the first time it's come up on this list.  Personally, I view it as a logic error and the results are undefined.  If you want to append a ListBuffer at a certain point in time to itself at the same point in time, why not write list ++ list.toList?
There are cases in Java like this.  The breakage that results from removing an element from a HashTable while iterating through the keys of the HashTable comes to mind. So, my vote would be to move on or at least move this discussion to debate.
Here is some code that highlights the problem. A library author offers a class that returns read-only lists of numbers. And the user can append more numbers to it. A user tries to double the numbers => kaboom.

class Numbers {
 import scala.collection.mutable.ListBuffer
 private val list = new ListBuffer[Int]
 def numbers: Seq[Int] = list
 def append(newNumbers: Seq[Int]): Unit = list ++ newNumbers
}

Why should the author of this have to check "if (newNumbers == list) ... " and do a custom work-around? Or why would he have to return defensive copies, even if the returned values are *not* modified outside? Or should the author document this?

Also I noticed some other similar behaviors in java collection wrappers, which is puzzling:

scala> import scala.collection.jcl.ArrayList
scala> val x = new ArrayList[AnyRef]
scala> x.add(x)
scala> x //throws StackOverflowError, the original java.util.ArrayList works fine
scala> x.hashCode //throws StackOverflowError, the original java.util.ArrayList works fine
//others? who knows

In any case, as Josh Bloch put it, we live in an economy of scale, and things get written once and used millions of times (well, not millions in this case but who knows what the future holds :-) ), so even the slightest effort that makes such a commonly-used library more convenient to use, pays off.

On Tue, Jan 13, 2009 at 4:06 AM, Dimitris Andreou <jim.andreou@gmail.com <mailto:jim.andreou@gmail.com>> wrote:

   O/H Seth Tisue ������:

                           "Dimitris" == Dimitris Andreou
                           <jim.andreou@gmail.com
                           <mailto:jim.andreou@gmail.com>> writes:
                                     

        Dimitris> I think this should be made to work:
        > val x = new ListBuffer[Int]
        > x += 1
        > x ++ x

        Dimitris> Now it throws an OOME.

       That seems like a reasonable outcome to me, actually.
       
   In Java, this works:
   List list = new ArrayList();
   list.add(1);
   list.addAll(list);
   //list is [1, 1]

   In my view, it's an implementation artifact (although an
   understandable one). Many similar problems regarding
   self-referencing collections existed in java.util classes, and
   they were all fixed (for example, a collections's toString() which
   contained itself, or collection.addAll(collection), etc).

   Otherwise it should be documented, saying that it can't handle
   itself as an argument (many other methods in Scala distribution
   already document the conditions under which they may not terminate).

   Dimitris






--
Lift, the simply functional web framework http://liftweb.net
Collaborative Task Management http://much4.us
Follow me: http://twitter.com/dpp
Git some: http://github.com/dpp

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