You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@uima.apache.org by Marshall Schor <ms...@schor.com> on 2009/08/11 03:00:18 UTC

clarifying a generics issue

Here's a new thread to discuss just one particular issue - a generics
tradeoff.

In other posts, people have expressed misgivings about letting users
"downcast" List<someType> to List<someSubType>, if it cannot be
*guaranteed* at compile time that this is valid.

Here's a simple example, for instance, using two built-in Java classes:
  Number
    Integer (a subtype of Number)

If we have a method which returns a List<Number>, and you happen to know
it only contains Integers, and you want to use a for-each on it with a
method that only exists in the Integer class, you have to do manual
casting within the loop.

An alternative might have been to do:
  List<Number> numbers = ...;
  List<Integer> int_version = numbers;  // not allowed, gives compile error

So once we're given the "numbers" object, as far as I can see, we're
stuck with its typing.  (Please tell me if I'm wrong here).

I see that there is a trade-off between specifying return types as
specific types, and specifying them as T extends X, and letting the user
specify a possibly incorrect downcast.  The tradeoff on the plus side is
that the user gets to write the new style for-each loops, and have code
without casts inside loops, etc., and on the minus side, the user when
doing this gets that warning about the possibility that at runtime a
casting error could be thrown.  But of course, that same casting error
could be thrown in restricted style, too, at the point of the explicit
user cast.

I'll probably stop trying to convince others if they continue to feel
that the tradeoffs here should be in the direction of returning only
specific types (disallowing users from specifying downcasting in that
manner), versus using types of the form T extends X,
which allows users to specify downcasting.

I'd be interested in any literature pointers discussing this trade-off
issue, if anyone has good ones :-)

-Marshall

Re: clarifying a generics issue

Posted by Thilo Goetz <tw...@gmx.de>.
Jörn Kottmann wrote:
> Jörn Kottmann wrote:
>> Marshall Schor wrote:
>>> I'll probably stop trying to convince others if they continue to feel
>>> that the tradeoffs here should be in the direction of returning only
>>> specific types (disallowing users from specifying downcasting in that
>>> manner), versus using types of the form T extends X,
>>> which allows users to specify downcasting.
>>>   
>> The case of FSIndexRepository is maybe more clear
>> then the other cases in our API.
>>
>> With down casting it could be changed to
>>
>> interface FSIndexRepository {
>> <T extends FeatureStructure> FSIndex<T> getIndex(String label);
>> <T extends FeatureStructure> FSIterator<T> getAllIndexedFS(Type aType);
>> ...
>> }
>>
>> and then a user could write
>>
>> FSIndex<AnnotationFS> index = repo.getIndex(...);
>> FSIterator<AnnotationFS> it = repo.getAllIndexedFS(...);
> Any opinions on this ? Right now the class is not generified but I
> would like to change that before the release.
> 
> Jörn

Will our average user know what this means?  If you
think the answer to that is "yes", then I'm for it.
I personally believe the answer is probably "no".

--Thilo

Re: clarifying a generics issue

Posted by Adam Lally <al...@alum.rpi.edu>.
On Wed, Aug 12, 2009 at 5:36 AM, Jörn Kottmann<ko...@gmail.com> wrote:
> Jörn Kottmann wrote:
>>
>> Marshall Schor wrote:
>>>
>>> I'll probably stop trying to convince others if they continue to feel
>>> that the tradeoffs here should be in the direction of returning only
>>> specific types (disallowing users from specifying downcasting in that
>>> manner), versus using types of the form T extends X,
>>> which allows users to specify downcasting.
>>>
>>
>> The case of FSIndexRepository is maybe more clear
>> then the other cases in our API.
>>
>> With down casting it could be changed to
>>
>> interface FSIndexRepository {
>> <T extends FeatureStructure> FSIndex<T> getIndex(String label);
>> <T extends FeatureStructure> FSIterator<T> getAllIndexedFS(Type aType);
>> ...
>> }
>>
>> and then a user could write
>>
>> FSIndex<AnnotationFS> index = repo.getIndex(...);
>> FSIterator<AnnotationFS> it = repo.getAllIndexedFS(...);
>
> Any opinions on this ? Right now the class is not generified but I
> would like to change that before the release.
>

I say no since like our other discussions, I don't like cases where
there's no guarantee of correctness.  A user can write:
FSIterator<AnnotationFS> it = repo.getAllIndexedFS(someNonAnnotationTypeCode);

and it would compile even though it doesn't make sense.

Something like this for JCas, on the other hand, does make sense to me:

FSIterator<T extends TOP> getAllIndexedFS(Class<T> jcasType)

which is called like
FSIterator<MyAnnotationType> it = jcas.getAllIndexedFS(MyAnnotationType.class);

 -Adam

Re: clarifying a generics issue

Posted by Jörn Kottmann <ko...@gmail.com>.
Jörn Kottmann wrote:
> Marshall Schor wrote:
>> I'll probably stop trying to convince others if they continue to feel
>> that the tradeoffs here should be in the direction of returning only
>> specific types (disallowing users from specifying downcasting in that
>> manner), versus using types of the form T extends X,
>> which allows users to specify downcasting.
>>   
> The case of FSIndexRepository is maybe more clear
> then the other cases in our API.
>
> With down casting it could be changed to
>
> interface FSIndexRepository {
> <T extends FeatureStructure> FSIndex<T> getIndex(String label);
> <T extends FeatureStructure> FSIterator<T> getAllIndexedFS(Type aType);
> ...
> }
>
> and then a user could write
>
> FSIndex<AnnotationFS> index = repo.getIndex(...);
> FSIterator<AnnotationFS> it = repo.getAllIndexedFS(...);
Any opinions on this ? Right now the class is not generified but I
would like to change that before the release.

Jörn

Re: clarifying a generics issue

Posted by Jörn Kottmann <ko...@gmail.com>.
Marshall Schor wrote:
> I'll probably stop trying to convince others if they continue to feel
> that the tradeoffs here should be in the direction of returning only
> specific types (disallowing users from specifying downcasting in that
> manner), versus using types of the form T extends X,
> which allows users to specify downcasting.
>   
The case of FSIndexRepository is maybe more clear
then the other cases in our API.

With down casting it could be changed to

interface FSIndexRepository {
<T extends FeatureStructure> FSIndex<T> getIndex(String label);
<T extends FeatureStructure> FSIterator<T> getAllIndexedFS(Type aType);
...
}

and then a user could write

FSIndex<AnnotationFS> index = repo.getIndex(...);
FSIterator<AnnotationFS> it = repo.getAllIndexedFS(...);

Jörn

Re: [VOTE] generics: have methods returning objects infer the type from the receiving variable type (was: clarifying a generics issue)

Posted by Adam Lally <al...@alum.rpi.edu>.
On Tue, Aug 11, 2009 at 3:16 PM, Marshall Schor<ms...@schor.com> wrote:
><snip/>
> So - can we close up this discussion with a vote on the following:
>
> On the normal scale of -1, 0, +1, to the proposition -
>
> have the generic form of many methods that are in the public API for
> UIMA that return UIMA objects, return a type which is inferred from the
> receiving typed variable,  versus having it return just one fixed type
> not inferred from the receiving typed variable,
>

-1

Adam

Re: [VOTE] generics: have methods returning objects infer the type from the receiving variable type

Posted by Jörn Kottmann <ko...@gmail.com>.
Marshall Schor wrote:
> On the normal scale of -1, 0, +1, to the proposition -
>
> have the generic form of many methods that are in the public API for
> UIMA that return UIMA objects, return a type which is inferred from the
> receiving typed variable,  versus having it return just one fixed type
> not inferred from the receiving typed variable,
>
> Marshall:  + .5   (somewhat in favor, but can live with not doing
> inference, perhaps implementing "internal" versions)
>
>   
-1, but there are a very few methods where type inferring
from the receiving typed variable would improve the calling
code.

Jörn

Re: [VOTE] generics: have methods returning objects infer the type from the receiving variable type

Posted by Thilo Goetz <tw...@gmx.de>.
Marshall Schor wrote:
> I can see Adam's point.
> 
> If there was some way to do an explicit cast that worked for
> collections, then I would be happy with that.
> 
> In other words, if we had something of type List<Number> and I knew the
> list really contained only Integers, if there was a way to cast this:
> 
>   List<Integers> i_know_what_i_m_doing = .... ( 
> (Magic_doesn't_really_exist_cast) something_returning_a_List<Number>))
> 
> then I would be happy.
> 
> It just seems to me to be too much to give up in the trade off - to lose
> the ability to have a List<Integers> when I know that's what I have.
>  
> That's the point I'm trying to make. 
> 
> One way around this, for instance of CASImpl and CAS etc., is to have
> alternative forms of the affected methods, such as
>    Iterator<CAS> getViewIterator()   // the public, normal one, and
>    Iterator<CASImpl> getInternalViewIterator()  // one that might be
> "public" but not part of the public API
> 
> The bad/sad part of this is this violates the write-it-once philosophy
> :-( ...
> 
> Of course, now that we have Java5 as a base, and given that
> getViewIterator actually constructs a real list object, and returns an
> iterator over it, it would make more sense in this case to have an
> "internal" method that returned the List, which implements Iterable,
> which would make it work in for-each loops :-)
> 
> So - can we close up this discussion with a vote on the following:
> 
> On the normal scale of -1, 0, +1, to the proposition -
> 
> have the generic form of many methods that are in the public API for
> UIMA that return UIMA objects, return a type which is inferred from the
> receiving typed variable,  versus having it return just one fixed type
> not inferred from the receiving typed variable,
> 
> Marshall:  + .5   (somewhat in favor, but can live with not doing
> inference, perhaps implementing "internal" versions)

-1

--Thilo


[VOTE] generics: have methods returning objects infer the type from the receiving variable type (was: clarifying a generics issue)

Posted by Marshall Schor <ms...@schor.com>.
I can see Adam's point.

If there was some way to do an explicit cast that worked for
collections, then I would be happy with that.

In other words, if we had something of type List<Number> and I knew the
list really contained only Integers, if there was a way to cast this:

  List<Integers> i_know_what_i_m_doing = .... ( 
(Magic_doesn't_really_exist_cast) something_returning_a_List<Number>))

then I would be happy.

It just seems to me to be too much to give up in the trade off - to lose
the ability to have a List<Integers> when I know that's what I have.
 
That's the point I'm trying to make. 

One way around this, for instance of CASImpl and CAS etc., is to have
alternative forms of the affected methods, such as
   Iterator<CAS> getViewIterator()   // the public, normal one, and
   Iterator<CASImpl> getInternalViewIterator()  // one that might be
"public" but not part of the public API

The bad/sad part of this is this violates the write-it-once philosophy
:-( ...

Of course, now that we have Java5 as a base, and given that
getViewIterator actually constructs a real list object, and returns an
iterator over it, it would make more sense in this case to have an
"internal" method that returned the List, which implements Iterable,
which would make it work in for-each loops :-)

So - can we close up this discussion with a vote on the following:

On the normal scale of -1, 0, +1, to the proposition -

have the generic form of many methods that are in the public API for
UIMA that return UIMA objects, return a type which is inferred from the
receiving typed variable,  versus having it return just one fixed type
not inferred from the receiving typed variable,

Marshall:  + .5   (somewhat in favor, but can live with not doing
inference, perhaps implementing "internal" versions)


Adam Lally wrote:
> On Tue, Aug 11, 2009 at 10:44 AM, Jörn Kottmann<ko...@gmail.com> wrote:
>   
>> Adam Lally wrote:
>>     
>> Lets say we have an interface Box{Number getNumber()}
>> then we could have a class BoxImpl implements Box{Integer getNumber(){...}}.
>>
>> Now I only would have to cast once
>> Box box = ...;
>> BoxImpl boxImpl = (BoxImpl) box;
>>
>> now
>> Integer number = boxImpl.getNumber()
>> would work. Sure if you do the same for CAS and CASImpl
>> you safe lots of casts.
>>
>> The same does not work for a getNumbers which
>> returns a List<Number> or Iterator<Number>
>> without also allowing down casting.
>>
>>     
>
> Well, true, but it does not really create an analogous solution if you do:
> interface Box{List<T extends Number> getNumberList()}
> class BoxImpl{List<Integer> numberList()}
>
> Because a client could call
> List<Integer> x = box.getNumberList()
>
> whether or not box was actually a BoxImpl.
>
>  -Adam
>
>
>   

Re: clarifying a generics issue

Posted by Adam Lally <al...@alum.rpi.edu>.
On Tue, Aug 11, 2009 at 10:44 AM, Jörn Kottmann<ko...@gmail.com> wrote:
> Adam Lally wrote:
>>
> Lets say we have an interface Box{Number getNumber()}
> then we could have a class BoxImpl implements Box{Integer getNumber(){...}}.
>
> Now I only would have to cast once
> Box box = ...;
> BoxImpl boxImpl = (BoxImpl) box;
>
> now
> Integer number = boxImpl.getNumber()
> would work. Sure if you do the same for CAS and CASImpl
> you safe lots of casts.
>
> The same does not work for a getNumbers which
> returns a List<Number> or Iterator<Number>
> without also allowing down casting.
>

Well, true, but it does not really create an analogous solution if you do:
interface Box{List<T extends Number> getNumberList()}
class BoxImpl{List<Integer> numberList()}

Because a client could call
List<Integer> x = box.getNumberList()

whether or not box was actually a BoxImpl.

 -Adam

Re: clarifying a generics issue

Posted by Jörn Kottmann <ko...@gmail.com>.
Adam Lally wrote:
> On Mon, Aug 10, 2009 at 9:00 PM, Marshall Schor<ms...@schor.com> wrote:
>   
>> Here's a new thread to discuss just one particular issue - a generics
>> tradeoff.
>>
>> In other posts, people have expressed misgivings about letting users
>> "downcast" List<someType> to List<someSubType>, if it cannot be
>> *guaranteed* at compile time that this is valid.
>>
>> Here's a simple example, for instance, using two built-in Java classes:
>>  Number
>>    Integer (a subtype of Number)
>>
>> If we have a method which returns a List<Number>, and you happen to know
>> it only contains Integers, and you want to use a for-each on it with a
>> method that only exists in the Integer class, you have to do manual
>> casting within the loop.
>>
>> An alternative might have been to do:
>>  List<Number> numbers = ...;
>>  List<Integer> int_version = numbers;  // not allowed, gives compile error
>>
>> So once we're given the "numbers" object, as far as I can see, we're
>> stuck with its typing.  (Please tell me if I'm wrong here).
>>
>> I see that there is a trade-off between specifying return types as
>> specific types, and specifying them as T extends X, and letting the user
>> specify a possibly incorrect downcast.  The tradeoff on the plus side is
>> that the user gets to write the new style for-each loops, and have code
>> without casts inside loops, etc., and on the minus side, the user when
>> doing this gets that warning about the possibility that at runtime a
>> casting error could be thrown.  But of course, that same casting error
>> could be thrown in restricted style, too, at the point of the explicit
>> user cast.
>>
>> I'll probably stop trying to convince others if they continue to feel
>> that the tradeoffs here should be in the direction of returning only
>> specific types (disallowing users from specifying downcasting in that
>> manner), versus using types of the form T extends X,
>> which allows users to specify downcasting.
>>
>> I'd be interested in any literature pointers discussing this trade-off
>> issue, if anyone has good ones :-)
>>
>>     
>
> One other way to look at this - why are Collections any different than
> methods that return single elements?  If I have a method
> Number getNumber()
>
> and I "know" in some situation that the Number is really an Integer,
> should I be able to call:
>
> Integer x = getNumber()?
>
> Normally such a thing is not allowed without an explicit cast.  And
> for me at least I just take that for granted as part of what a
> strongly-typed language does, so it's very bizarre to think of it not
> doing that.
>
>   
Lets say we have an interface Box{Number getNumber()}
then we could have a class BoxImpl implements Box{Integer getNumber(){...}}.

Now I only would have to cast once
Box box = ...;
BoxImpl boxImpl = (BoxImpl) box;

now
Integer number = boxImpl.getNumber()
would work. Sure if you do the same for CAS and CASImpl
you safe lots of casts.

The same does not work for a getNumbers which
returns a List<Number> or Iterator<Number>
without also allowing down casting.

Jörn

Re: clarifying a generics issue

Posted by Adam Lally <al...@alum.rpi.edu>.
On Mon, Aug 10, 2009 at 9:00 PM, Marshall Schor<ms...@schor.com> wrote:
> Here's a new thread to discuss just one particular issue - a generics
> tradeoff.
>
> In other posts, people have expressed misgivings about letting users
> "downcast" List<someType> to List<someSubType>, if it cannot be
> *guaranteed* at compile time that this is valid.
>
> Here's a simple example, for instance, using two built-in Java classes:
>  Number
>    Integer (a subtype of Number)
>
> If we have a method which returns a List<Number>, and you happen to know
> it only contains Integers, and you want to use a for-each on it with a
> method that only exists in the Integer class, you have to do manual
> casting within the loop.
>
> An alternative might have been to do:
>  List<Number> numbers = ...;
>  List<Integer> int_version = numbers;  // not allowed, gives compile error
>
> So once we're given the "numbers" object, as far as I can see, we're
> stuck with its typing.  (Please tell me if I'm wrong here).
>
> I see that there is a trade-off between specifying return types as
> specific types, and specifying them as T extends X, and letting the user
> specify a possibly incorrect downcast.  The tradeoff on the plus side is
> that the user gets to write the new style for-each loops, and have code
> without casts inside loops, etc., and on the minus side, the user when
> doing this gets that warning about the possibility that at runtime a
> casting error could be thrown.  But of course, that same casting error
> could be thrown in restricted style, too, at the point of the explicit
> user cast.
>
> I'll probably stop trying to convince others if they continue to feel
> that the tradeoffs here should be in the direction of returning only
> specific types (disallowing users from specifying downcasting in that
> manner), versus using types of the form T extends X,
> which allows users to specify downcasting.
>
> I'd be interested in any literature pointers discussing this trade-off
> issue, if anyone has good ones :-)
>

One other way to look at this - why are Collections any different than
methods that return single elements?  If I have a method
Number getNumber()

and I "know" in some situation that the Number is really an Integer,
should I be able to call:

Integer x = getNumber()?

Normally such a thing is not allowed without an explicit cast.  And
for me at least I just take that for granted as part of what a
strongly-typed language does, so it's very bizarre to think of it not
doing that.

 -Adam

Re: clarifying a generics issue

Posted by Adam Lally <al...@alum.rpi.edu>.
On Tue, Aug 11, 2009 at 7:50 AM, Marshall Schor<ms...@schor.com> wrote:
> A small correction: allowing downcasting doesn't generate a warning
> about unchecked casting.
>
> Here's a small test case in case you want to play with this:
>
> package generics;
>
> import java.util.List;
>
> public class TestG {
>
>  class Box<T> {  // version returning down-castable thing
>    <U extends T> U get() {return null;}
>    <U extends T> List<U> getL() {return null;}
>  }
>
>  class StrictBox<T> {  // version returning just type T
>    T get() {return null;}
>    List<T> getL() {return null;}
>  }
>
>  Box<Number> box = new Box<Number>();
>  StrictBox<Number> sbox = new StrictBox<Number>();
>
>  void test() {
>    // returning elements
>    Number n = box.get();   // OK
>    Integer n2 = box.get(); // OK
>
>    Number sn = sbox.get();   // OK
>    Integer sn2 = sbox.get(); // fails
>    Integer sn3 = (Integer) sbox.get(); // OK
>
>    // returning lists
>    List<Number> ln = box.getL();   // OK
>    List<Integer> ln2 = box.getL(); // OK
>
>    List<Number> sln = sbox.getL(); // OK
>    List<Integer> sln2 = sbox.getL(): // fails, no fix available
>  }
> }
>

For a complete picture you should also try to implement the methods to
return something other that null.  For example how to you implement
<U extends T> U get() such that it returns an instance of U regardless
of what U is?  You could only do this with a typecast.  And you'd have
to expectation that the typecast would actually succeed.

  -Adam

Re: clarifying a generics issue

Posted by Marshall Schor <ms...@schor.com>.
A small correction: allowing downcasting doesn't generate a warning
about unchecked casting.

Here's a small test case in case you want to play with this:

package generics;

import java.util.List;

public class TestG {

  class Box<T> {  // version returning down-castable thing
    <U extends T> U get() {return null;}
    <U extends T> List<U> getL() {return null;}
  }
 
  class StrictBox<T> {  // version returning just type T
    T get() {return null;}
    List<T> getL() {return null;}
  }

  Box<Number> box = new Box<Number>(); 
  StrictBox<Number> sbox = new StrictBox<Number>();
 
  void test() {
    // returning elements
    Number n = box.get();   // OK
    Integer n2 = box.get(); // OK
   
    Number sn = sbox.get();   // OK
    Integer sn2 = sbox.get(); // fails
    Integer sn3 = (Integer) sbox.get(); // OK
   
    // returning lists
    List<Number> ln = box.getL();   // OK
    List<Integer> ln2 = box.getL(); // OK
   
    List<Number> sln = sbox.getL(); // OK
    List<Integer> sln2 = sbox.getL(): // fails, no fix available  
  }
}

-Marshall

Marshall Schor wrote:
> Here's a new thread to discuss just one particular issue - a generics
> tradeoff.
>
> In other posts, people have expressed misgivings about letting users
> "downcast" List<someType> to List<someSubType>, if it cannot be
> *guaranteed* at compile time that this is valid.
>
> Here's a simple example, for instance, using two built-in Java classes:
>   Number
>     Integer (a subtype of Number)
>
> If we have a method which returns a List<Number>, and you happen to know
> it only contains Integers, and you want to use a for-each on it with a
> method that only exists in the Integer class, you have to do manual
> casting within the loop.
>
> An alternative might have been to do:
>   List<Number> numbers = ...;
>   List<Integer> int_version = numbers;  // not allowed, gives compile error
>
> So once we're given the "numbers" object, as far as I can see, we're
> stuck with its typing.  (Please tell me if I'm wrong here).
>
> I see that there is a trade-off between specifying return types as
> specific types, and specifying them as T extends X, and letting the user
> specify a possibly incorrect downcast.  The tradeoff on the plus side is
> that the user gets to write the new style for-each loops, and have code
> without casts inside loops, etc., and on the minus side, the user when
> doing this gets that warning about the possibility that at runtime a
> casting error could be thrown.  But of course, that same casting error
> could be thrown in restricted style, too, at the point of the explicit
> user cast.
>
> I'll probably stop trying to convince others if they continue to feel
> that the tradeoffs here should be in the direction of returning only
> specific types (disallowing users from specifying downcasting in that
> manner), versus using types of the form T extends X,
> which allows users to specify downcasting.
>
> I'd be interested in any literature pointers discussing this trade-off
> issue, if anyone has good ones :-)
>
> -Marshall
>
>
>