Indexing access for Lists and Maps considered harmful?

Shams Mahmood shams.mahmood at gmail.com
Tue Jun 23 13:55:54 PDT 2009


I'm late to join the party too (at the mailing list).

One of my responses to the blog post was as follows:

A method of maintaining the consistency and backwards compatibility could be
to implement utility methods in say for e.g. Collections class:
public static V get(Gettable<K, V> pObject, K pKey) {…}
public static V set(Puttable<K, V> pObject, K pKey, V pNewValue) {…}
public static V set(Map<K, V> pObject, K pKey, V pNewValue) {…}
public static V get(GettableFromInt<V> pObject, int pIndex) {…}
public static V set(SettableViaInt<V> pObject, int pIndex, V pNewValue) {…}
public static V set(List<V> pObject, int pIndex, V pNewValue) {…}

and also have Maps implementing Gettable and Puttable and Lists implementing
GettableFromInt and SettableViaInt.

The compiler will translate appropriately to corresponding method calls
during compilation.
This solution can easily be extended to have support for custom classes with
the indexing access if they implement the appropriate interfaces.

Reading furter discussion here, probably IndexedAccess<V> and
DictionaryAccess<K, V> interfaces will do the trick with special utility
methods for List and Map to wrap around the set/put methods
So the new methods would be:
  public static V get(DictionaryAccess<K, V> pObject, K pKey) {…}
  public static V set(DictionaryAccess<K, V> pObject, K pKey, V pNewValue)
{…}
  public static V get(IndexedAccess<V> pObject, int pIndex) {…}
  public static V set(IndexedAccess<V> pObject, int pIndex, V pNewValue) {…}

e.g.
public static V set(IndexedAccess<V> pObject, int pIndex, V pNewValue) {
  pObject.set(pIndex, pNewValue);
  return pNewValue;
}


So,
List<String> list = new ArrayList();
list[1] = list[0] = "value";
would get translated to:
Collections.set(list, 1, Collections.set(list, 0, "value"))


Shams


> ---------- Forwarded message ----------
> From: Artur Biesiadowski <abies at adres.pl>
> To: coin-dev <coin-dev at openjdk.java.net>
> Date: Tue, 23 Jun 2009 20:02:37 +0200
> Subject: Re: Indexing access for Lists and Maps considered harmful?
> Joseph D. Darcy wrote:
>
>> // New superinterfaces to indicate syntax?
>> interface GettableFromInt<E> {
>> E get(int);
>> }
>>
>> interface Gettable<K, V> {
>> V get(K k);
>> }
>>
>> interface Settable<E> {
>> E set(int, E e);
>> }
>> interface Puttable<K, V> {
>> V put(K k, V v);
>> }
>>
>>
> If anything like that is implemented, I would rather see it as two
> interfaces - ListAccess and MapAccess (or maybe IndexAccess and
> Key/DictionaryAccess to be less collection-oriented?). Read-only or
> write-only classes can throw runtime exceptions if needed.
>
> Do you think that one-operation classes would be common? I can see a
> possible benefit of detecting this at compile time instead of runtime, but
> how useful it would be in real world...
>
> All collection classes would be read-write (even for immutable collections
> no way to indicate it on interface level).
> String could be probably made 'GettableFromInt'.
> StringBuilder/Buffer is already read-write. Same goes for BitSet.
>
> I think it boils down to question if we want to have
>
> String txt = "ABC";
> txt[0] = 'X';
>
> failing at compile time or runtime. On the other hand, in case of String, I
> could imagine most IDEs making it a warning at compile time easily...
>
> Regards,
> Artur Biesiadowski
>
>
>
> ---------- Forwarded message ----------
> From: "Joseph D. Darcy" <Joe.Darcy at Sun.COM>
> To: Reinier Zwitserloot <reinier at zwitserloot.com>
> Date: Tue, 23 Jun 2009 11:06:20 -0700
> Subject: Re: Indexing access for Lists and Maps considered harmful?
> Reinier Zwitserloot wrote:
>
>> You realize this is going to be an instant java puzzler, right? I thought
>> one of the soft goals of project coin is to attempt to sidestep obvious
>> puzzlers.
>>
>
> It may be the case that changing the desugaring to return void in these
> cases is the right thing to do in the end.
>
> However, the mere existence of *some* puzzler should not be sufficient to
> derail a coin proposal.  I fully expect the language changes to enable new,
> initially unexpected program behavior; that does have to be more than
> compensated by the new, expected programs behavior that is allowed of course
> :-)
>
> -Joe
>
>
>> I also admit that I cannot come up with any situation where the
>> expression:
>>
>> thingA[thingB] = thingC;
>>
>> evaluating to something that ISNT 'thingC' (or void) is going to be the
>> logical, not confusing choice.
>>
>>
>> Suggestion: Define this operation to return void regardless of the
>> underlying method implementation's actual return value. That way, if in the
>> future a solid use case for letting the method invocation decide return type
>> and value is found, a change can be made without breaking old code. If, on
>> the other hand, there's a clamour for letting this expression return
>> 'thingC', that can be added to a future version of java as well.
>>
>> In other words, picking EITHER "That evaluates to thingC" OR "That
>> evaluates to whatever the put method returns" is a choice you must make for
>> the rest of the life of the java language. On the other hand, picking "That
>> is a statement and not an expression (e.g. evaluates to void)" is not a
>> life-long choice; if it ends up being a bad choice, you can switch to either
>> alternative later without any pain other than filing a purely additive
>> language change.
>>
>>
>> The only obvious disadvantage to 'return void' that I can see is that its
>> inconsistent with both the current behaviour of the assignment operator AND
>> the desugaring idea, whereas the alternatives are consistent with one
>> behaviour and inconsistent with the other. Then again, with 'returns thingC'
>> and 'returns returntype of put method', the inconsistent behaviour is
>> silent, puzzler-esque, and going to cause hundreds of people subtle and hard
>> to find bugs, whereas in the 'returns void' scenario, there is no confusion
>> at all, as the compiler/editor will instantly tell you that the assignment
>> is a statement and not an expression.
>>
>>
>> I just talked myself into throwing my overwhelming support behind the
>> return void choice. Both other choices lead to long bug hunts and confusion
>> that can only be solved by experimenting or grabbing the JLS, whereas void
>> can't lead to bug hunts and any confusion it engenders is instantly assuaged
>> by a compiler hint.
>>
>>  --Reinier Zwitserloot
>>
>>
>>
>> On 2009/23/06, at 07:46, Joseph D. Darcy wrote:
>>
>>  My current preference is to keep a simple desugaring of the indexing
>>> operators into method calls.  While collection usage would certainly be a
>>> common case, there are probably other to-be-written APIs that would benefit
>>> from the translation into method calls.
>>>
>>> -Joe
>>>
>>> Reinier Zwitserloot wrote:
>>>
>>>> I agree that the expression "map[c] = d" returning the OLD value of
>>>>  map[c] (e.g. usually null) is unacceptable behaviour, but I think this
>>>>  propsoal would fare just fine if this form of operation is defined to
>>>>  return void and not 'd', in order to avoid confusion. There are many  code
>>>> bases that never use pass-through assignment, and the workaround  (write it
>>>> out...) is by definition not more boilerplate and noise than  what we have
>>>> now.
>>>>
>>>>
>>>> I do lean slightly to preferring the expression to return 'd', and not
>>>>  void, nor 'old mapping of c'.
>>>>
>>>>  --Reinier Zwitserloot
>>>>
>>>>
>>>>
>>>> On 2009/22/06, at 22:40, Lawrence Kesteloot wrote:
>>>>
>>>>
>>>>  On Mon, Jun 22, 2009 at 12:19 PM, Joseph D. Darcy<Joe.Darcy at sun.com>
>>>>>  wrote:
>>>>>
>>>>>  map["a"] = map["b"] = "c";
>>>>>>
>>>>>> the new value for key "a" would be the old mapping of "b" and not
>>>>>> necessarily "c" because a call to the put method returns the old
>>>>>>  value.
>>>>>>
>>>>>>  There is no way that:
>>>>>
>>>>>  map["b"] = "c"
>>>>>
>>>>> can return anything other than "c". Returning the old value or
>>>>> returning void would violate 40 years of C-based expectations
>>>>> (including the identical construct in the STL). It must either return
>>>>> "c" or not be implemented at all.
>>>>>
>>>>> Lawrence
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>
>>
>
>
>
> ---------- Forwarded message ----------
> From: "Joseph D. Darcy" <Joe.Darcy at Sun.COM>
> To: Neal Gafter <neal at gafter.com>
> Date: Tue, 23 Jun 2009 11:12:14 -0700
> Subject: Re: Indexing access for Lists and Maps considered harmful?
> Neal Gafter wrote:
>
>> Why would a language-support method like Puttable.put return a value that
>> the generated code is required to ignore?
>>
>
> I am not proposing that the value is required to be ignored.  My current
> thinking is that compiler warnings are sufficient to deal with the multiple
> assignment situation in indexed access.  Aggregates outside
> java.util.Collection could choose to be Puttable<KeyType, Void.class> to
> avoid having to return some value.
>
> -Joe
>
>  From the point of view of designing a language-support API, it makes no
>> sense whatsoever.  The return type is vestigal: it is only present because
>> of the way that we are considering retrofitting it onto some existing APIs,
>> but it forces any new APIs that are retrofitted to return some (arbitrary)
>> value to be ignored.  It also imposes a performance penalty on
>> implementations that can more efficiently overwrite a mapping than overwrite
>> AND return the old value.  This is a language-design smell that suggests we
>> should consider finding another strategy for getting the effect of index
>> operators.
>>
>> On Tue, Jun 23, 2009 at 10:36 AM, Joseph D. Darcy <Joe.Darcy at sun.com<mailto:
>> Joe.Darcy at sun.com>> wrote:
>>
>>    spamolovko wrote:
>>
>>    [snip]
>>    >
>>    > PS i remember that coin project had discussion about usage of
>>    special
>>    > interface for "indexing access". That interface have to use it's own
>>    > methods for access to data, so no problems with get semantic.
>>    >
>>    > Example:
>>    >
>>    > public interface Indexer<KeyType, ValueType> {
>>    >
>>    >     public ValueType indexGet(KeyType key);
>>    >
>>    >     public void indexSet(KeyType key, ValueType value);
>>    >
>>    > }
>>    >
>>
>>    [snip]
>>
>>    The idea would be to retrofit new superinterfaces onto the List
>>    and Map
>>    interfaces without adding new methods to the interfaces. For example,
>>    here is a slight refinement of strawman interfaces of that sort
>>    from my
>>    JavaOne talk:
>>
>>    // New superinterfaces to indicate syntax?
>>    interface GettableFromInt<E> {
>>    E get(int);
>>    }
>>
>>    interface Gettable<K, V> {
>>    V get(K k);
>>    }
>>
>>    interface Settable<E> {
>>    E set(int, E e);
>>    }
>>    interface Puttable<K, V> {
>>    V put(K k, V v);
>>    }
>>
>>    java.util.List<E> extends GettableFromInt<E>, Settable<E>, …
>>
>>    java.util.Map<K, V> extends Gettable<Object, V>, Puttable(K, V), …
>>
>>    The names "Gettable" etc. are open to improvement :-)
>>
>>    -Joe
>>
>>
>>
>
>
>
> ---------- Forwarded message ----------
> From: Neal Gafter <neal at gafter.com>
> To: "Joseph D. Darcy" <Joe.Darcy at sun.com>
> Date: Tue, 23 Jun 2009 11:20:41 -0700
> Subject: Re: Indexing access for Lists and Maps considered harmful?
> On Tue, Jun 23, 2009 at 11:12 AM, Joseph D. Darcy <Joe.Darcy at sun.com>
> wrote:
>
> > Neal Gafter wrote:
> >
> >> Why would a language-support method like Puttable.put return a value
> that
> >> the generated code is required to ignore?
> >>
> >
> > I am not proposing that the value is required to be ignored.  My current
> > thinking is that compiler warnings are sufficient to deal with the
> multiple
> > assignment situation in indexed access.  Aggregates outside
> > java.util.Collection could choose to be Puttable<KeyType, Void.class> to
> > avoid having to return some value.
>
>
> That still requires the implementation to return something to be ignored
> (null), and is not parallel the the language's existing semantics for
> assignment expressions.
>
>
>
> ---------- Forwarded message ----------
> From: Joshua Bloch <jjb at google.com>
> To: coin-dev <coin-dev at openjdk.java.net>
> Date: Tue, 23 Jun 2009 11:29:58 -0700
> Subject: Re: Indexing access for Lists and Maps considered harmful?
> Folks,
>
> Hi.  Sorry I'm late to the party; I'm just recovering from a pretty bad
> cold.
>
> I agree that this is not a showstopper.
>
> I think it's *critical* that the behavior be defined to match the
> "expected"
> behavior.  These *must* function identically:
>
>   myArray[i] = myArray[j] = val;
>
>   myList[i] = myList[j] = val;
>
> When extending a language, it's critical to enable reasoning by analogy; to
> do otherwise *will* cause bugs and ill-will.  (See this 20-year-old paper
> for my take on the topic:
> http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=37945 .)
>
>    Josh
>
>
>
> On Mon, Jun 22, 2009 at 12:33 PM, Tim Peierls <tim at peierls.net> wrote:
>
> > Cool way to do swap and cyclic shift without temporaries:
> >  m[a] = m[b] = m[c] = m[a];
> >
> > I love it! Can I have it, can I, huh? :-)
> >
> > Seriously, I don't think this is a showstopper.
> >
> > --tim
> >
> > On Mon, Jun 22, 2009 at 3:19 PM, Joseph D. Darcy <Joe.Darcy at sun.com>
> > wrote:
> >
> > > Hello.
> > >
> > > A recent blog post cited in Alex Miller's handy "Java 7 Links"
> > > (http://java7.tumblr.com) claims that adding indexing support for
> Lists
> > > and Maps would be harmful because of an arguably surprising behavior on
> > > compound assignment.  After
> > >
> > > map["a"] = map["b"] = "c";
> > >
> > > the new value for key "a" would be the old mapping of "b" and not
> > > necessarily "c" because a call to the put method returns the old value.
> > >
> > > I'm not too concerned about this interaction because compound
> assignment
> > > is relatively infrequent; however, I think a lint warning from the
> > > compiler would be appropriate in this case.  Also, an IDE could color
> > > the non-array uses of "[]" differently to highlight any potential
> > > differences in semantics.
> > >
> > > As a meta-comment, I find it odd that if someone wanted to inform the
> > > coin discussion he or she would not directly email the coin list in
> > > addition to or instead of posting a blog entry.
> > >
> > > -Joe
> > >
> > >
> >
> >
>
>
>
> ---------- Forwarded message ----------
> From: "Rémi Forax" <forax at univ-mlv.fr>
> To: "Joseph D. Darcy" <Joe.Darcy at Sun.COM>
> Date: Tue, 23 Jun 2009 20:40:37 +0200
> Subject: Re: Indexing access for Lists and Maps considered harmful?
> Joseph D. Darcy a écrit :
>
>> Neal Gafter wrote:
>>
>>
>>> Why would a language-support method like Puttable.put return a value that
>>> the generated code is required to ignore?
>>>
>>
>> I am not proposing that the value is required to be ignored.  My current
>> thinking is that compiler warnings are sufficient to deal with the multiple
>> assignment situation in indexed access.
>>
>
> Please, no more warning, warnings are not a clear signal for an average
> Joe.
>
> I am against the use of interfaces like Gettable/Puttable because you will
> have to
> rely on generics which doesn't mix well with primitive types.
>
> Some people that are fan of Trove :
> http://trove4j.sourceforge.net/index.html
>
> Rémi
>
>   Aggregates outside java.util.Collection could choose to be
>> Puttable<KeyType, Void.class> to avoid having to return some value.
>>
>> -Joe
>>
>>
>>
>>> From the point of view of designing a language-support API, it makes no
>>> sense whatsoever.  The return type is vestigal: it is only present because
>>> of the way that we are considering retrofitting it onto some existing APIs,
>>> but it forces any new APIs that are retrofitted to return some (arbitrary)
>>> value to be ignored.  It also imposes a performance penalty on
>>> implementations that can more efficiently overwrite a mapping than overwrite
>>> AND return the old value.  This is a language-design smell that suggests we
>>> should consider finding another strategy for getting the effect of index
>>> operators.
>>>
>>> On Tue, Jun 23, 2009 at 10:36 AM, Joseph D. Darcy <Joe.Darcy at sun.com<mailto:
>>> Joe.Darcy at sun.com>> wrote:
>>>
>>>    spamolovko wrote:
>>>
>>>    [snip]
>>>    >
>>>    > PS i remember that coin project had discussion about usage of
>>>    special
>>>    > interface for "indexing access". That interface have to use it's own
>>>    > methods for access to data, so no problems with get semantic.
>>>    >
>>>    > Example:
>>>    >
>>>    > public interface Indexer<KeyType, ValueType> {
>>>    >
>>>    >     public ValueType indexGet(KeyType key);
>>>    >
>>>    >     public void indexSet(KeyType key, ValueType value);
>>>    >
>>>    > }
>>>    >
>>>
>>>    [snip]
>>>
>>>    The idea would be to retrofit new superinterfaces onto the List
>>>    and Map
>>>    interfaces without adding new methods to the interfaces. For example,
>>>    here is a slight refinement of strawman interfaces of that sort
>>>    from my
>>>    JavaOne talk:
>>>
>>>    // New superinterfaces to indicate syntax?
>>>    interface GettableFromInt<E> {
>>>    E get(int);
>>>    }
>>>
>>>    interface Gettable<K, V> {
>>>    V get(K k);
>>>    }
>>>
>>>    interface Settable<E> {
>>>    E set(int, E e);
>>>    }
>>>    interface Puttable<K, V> {
>>>    V put(K k, V v);
>>>    }
>>>
>>>    java.util.List<E> extends GettableFromInt<E>, Settable<E>, …
>>>
>>>    java.util.Map<K, V> extends Gettable<Object, V>, Puttable(K, V), …
>>>
>>>    The names "Gettable" etc. are open to improvement :-)
>>>
>>>    -Joe
>>>
>>>
>>>
>>>
>>
>>
>>
>>
>
>
>
>


-- 
Shams Mahmood



More information about the coin-dev mailing list