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 <[email protected]>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 <[email protected]>
>
>> 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 <[email protected]> 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 <[email protected]> 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
>>>> <[email protected]>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 <[email protected]>
>>>>>
>>>>> 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.