public MyPanel(ImmutableArray<Widget> widgets) { ... } That's the use case I was missing. Thanks for taking the time to debate, guys.
rjrjr On Mon, Mar 22, 2010 at 4:05 PM, Bruce Johnson <br...@google.com> wrote: > I think Rodrigo's point already subsumed what I'm about to say, but there > are three cases here: > > 1) A read-only reference to a collection that may or may not be mutable by > someone else. This is the purpose of the root type Array, which has not > mutators but doesn't make a guarantee about whether the referenced > collection is mutable or immutable. > > 2) A reference to a read-only collection whose contents are guaranteed to > in fact be immutable. > > 3) A normal, mutable array that is read/write. > > @Ray: The idea of an immutableView() method doesn't seem to quite handle > these use cases, because either the returned object is a separate object > with a runtime wrapper (the equivalent of unmodifiableList()) or it's a > read-only interface to a collection that isn't really guaranteed to be > immutable, merely read-only. Also note that MutableArray extends the > read-only Array class, and so every MutableArray can be implicitly returned > as a read-only alias anywhere you want, which is even easier than > immutableView(). > > I would guess it would be fairly typical to use mostly MutableArray and > Array, but ImmutableArray has a real role, too. Imagine a MyPanel that takes > a list of Widgets in its constructor. You could declare the constructor like > this: > > public MyPanel(ImmutableArray<Widget> widgets) { ... } > > That's useful because you can code the class with full knowledge that the > set of widgets won't be changing out from under you from the calling code -- > you can just directly assign that param to a field without making a copy. > Had the constructor been: > > public MyPanel(List<Widget> widgets) { ... } > > you could never be sure and would probably feel compelled to make a > defensive copy. > > I'm going to guess this is how it would play out: > > 1) Constructors will often take ImmutableArray because it's nice to assign > ctor params directly to fields and know for a fact there's no reason to make > defensive copies. > > 2) Fields will be declared as MutableArray, and in getters the return type > will be Array, thus allowing getters to safely return references directly, > happy in the knowledge that there is neither a need to make a defensive > outgoing copy nor a risk that the caller can mutate your private field's > contents. > > 3) Library methods will take the least-specific type that can suffice for > the respective algorithm. For example, binarySearch(), find(), forEach(), > etc. would take a param of type Array, thus allowing either mutable or > immutable arrays to use the method. Then again, sort() would explicitly take > a MutableArray. > > And of course, there is no desire to design all this in a vacuum -- and we > shouldn't argue against it in a vacuum either. This is a starting point > strawman, so I'd propose we use this design as a stake in the ground and > then see how it would play it if we retrofitted it into real code, both app > code and library code. Then we'll really get a feel for how useful vs. > confusing it is, and most importantly, we can get metrics on the speed and > size. > > > > On Mon, Mar 22, 2010 at 6:34 PM, Rodrigo Chandia <rchan...@google.com>wrote: > >> Immutability is a stronger assertion than read-only access. If I receive a >> read-only object I better make sure to handle the case of the data being >> changed by others; be it by tacit agreement, using other channels, locking >> or simply ignoring the issue. Immutability guarantees the data is stable and >> unchanging. The agreement is explicit. I will be able to read it to my >> heart's content without worry. >> >> Granted, being this a simple implementation it has some inconveniences: >> >> 1. The Immutability is achieved by "freezing" the original Mutable >> container while it may be useful to keep working with the original container >> instead. >> 2. The receiver of a MutableArray may find later that someone called >> freeze on the object causing an error. >> >> This is largely a matter of it being a lightweight implementation. There >> is no contract forcing the mutable view to be tied to the original mutable >> container. A more sophisticated implementation could copy the container, >> implement copy-on-write semantics, or any number of alternative >> implementations. Still the meaning of an immutable collection implies the >> contents will not change, while a read-only view makes no such promise. >> >> 2010/3/22 Ray Ryan <rj...@google.com> >> >>> My argument is that one is necessary and sufficient. Two is kind of >>> pointless if you have achieved one, and maybe even counterproductive. >>> >>> >>> On Mon, Mar 22, 2010 at 2:32 PM, Joel Webber <j...@google.com> wrote: >>> >>>> I think we're talking about two different things here. Rodrigo's (valid) >>>> point is that implementing immutability sanely early on is a good idea. And >>>> this implementation is pretty much analogous to the one you describe from >>>> Cocoa. >>>> >>>> The question at hand is whether it makes sense to get an immutable >>>> collection from a mutable one, with no copies. There are two ways to do >>>> this: >>>> 1. Create an immutable "view" of a mutable list, but with no guarantees >>>> that the list won't be mutated by the original owner later. >>>> 2. "Freeze" a mutable list into an immutable view of said list, making >>>> the former "runtime immutable". >>>> >>>> (1) solves the problem of a class giving access to one of its internal >>>> collections without having to guard against external mutation. (2) can be >>>> used to replicate the "builder" pattern. >>>> >>>> I don't have a strong opinion about (2) myself, but (1) is pretty damned >>>> important, because it's the source of innumerable stupid defensive copies >>>> in >>>> JRE code. The provider of such an interface would just have to be very >>>> clear >>>> about whether the "immutable" list might be modified later (because it's a >>>> view on a mutable one). >>>> >>>> On Mon, Mar 22, 2010 at 5:23 PM, Ray Ryan <rj...@google.com> wrote: >>>> >>>>> I think you're missing my point. An object is immutable if there exists >>>>> no api to mutate it. That should be enough. >>>>> >>>>> Let me put it another way. It's lame that the JRE achieves immutability >>>>> by turning mutate methods into runtime errors. It will be equally lame of >>>>> us >>>>> to do the same, especially since we can't enforce it at production time. >>>>> It >>>>> would be much better to provide an api such that there is not even >>>>> possible >>>>> to compile the mutate call (without cheating with casts, but then you know >>>>> you're being bad). >>>>> >>>>> The Cocoa approach to this is to have interfaces like NSArray be >>>>> immutable, and then have NSMutableArray extend NSArray. If we're going to >>>>> roll our own collection classes, it seems to me we could do the same: e.g. >>>>> LiteImmutableList and List extends LiteImmutableList. >>>>> >>>>> rjrjr >>>>> >>>>> >>>>> On Mon, Mar 22, 2010 at 2:12 PM, Rodrigo Chandia >>>>> <rchan...@google.com>wrote: >>>>> >>>>>> I like the *concept* of immutability being introduced early in the >>>>>> development. The initial implementation may be limiting for some use >>>>>> cases, >>>>>> but I believe it is a useful concept to expand on. If specific needs >>>>>> require >>>>>> simultaneous mutable and immutable access we can provide implementations >>>>>> to >>>>>> address that problem (copy on write semantics, for example). >>>>>> >>>>>> 2010/3/22 Ray Ryan <rj...@google.com> >>>>>> >>>>>> I guess I'm overstating my opposition. It's not really dangerous, but >>>>>>> it just doesn't seem useful. Just by existing I think it'll promote >>>>>>> confusion and perhaps bad habits. Why bother? >>>>>>> >>>>>>> I think the 90% use case is for something like the following (writing >>>>>>> in JRE terms here): >>>>>>> >>>>>>> private final List<String> magicValues; >>>>>>> { >>>>>>> List<String> buildValues = new ArrayList<String>(); >>>>>>> buildValues.add("able"); >>>>>>> buildValues.add("baker"); >>>>>>> buildValues.add("charlie"); >>>>>>> magicValues = Collections.unmodifiableList(buildValues); >>>>>>> } >>>>>>> >>>>>>> Ta da: it's a read only structure and no copy was made. In our world, >>>>>>> we could do better: >>>>>>> >>>>>>> private final ImmutableLiteList<String> magicValues; >>>>>>> { >>>>>>> LiteList<String> buildValues = new LiteList<String>(); >>>>>>> buildValues.add("able"); >>>>>>> buildValues.add("baker"); >>>>>>> buildValues.add("charlie"); >>>>>>> magicValues = buildValues.immutableView(); // more equivalent of >>>>>>> cast() >>>>>>> } >>>>>>> >>>>>>> The user never thinks in terms of freezing, just cutting off access. >>>>>>> No extra dev mode mechanism to maintain, and basically the same idiom >>>>>>> already in use in Java. >>>>>>> >>>>>> >>>>>> >>>>> -- >>>>> http://groups.google.com/group/Google-Web-Toolkit-Contributors >>>>> >>>>> To unsubscribe from this group, send email to >>>>> google-web-toolkit-contributors+unsubscribegooglegroups.com or reply >>>>> to this email with the words "REMOVE ME" as the subject. >>>>> >>>> >>>> -- >>>> http://groups.google.com/group/Google-Web-Toolkit-Contributors >>>> >>>> To unsubscribe from this group, send email to >>>> google-web-toolkit-contributors+unsubscribegooglegroups.com or reply to >>>> this email with the words "REMOVE ME" as the subject. >>>> >>> >>> >> > -- http://groups.google.com/group/Google-Web-Toolkit-Contributors To unsubscribe from this group, send email to google-web-toolkit-contributors+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.