incubator-river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jgr...@simulexinc.com
Subject Re: Space/outrigger suggestions
Date Fri, 17 Dec 2010 16:10:36 GMT
I think we're approaching a common understanding.

I'm familiar with type erasure, and am aware that my proposed method signature erases down
to the current method signature.

When I spoke of the additional specification power and type safety, I wasn't speaking at the
byte-code level, but at the java-code level.   That generics aren't reified does not mean
that they lack value.

So if I now understand your primary argument against using generics in Javaspace/RiverSpace's
method signatures is a slippery slope argument?   That an implementer/user who sees the use
of generics might get carried away, use them incorrectly elsewhere, and introduce a bug within
their own code?

I think it presumes too much, to sacrifice specificity and clarity in service of a second-order
effect filtered through an unknown developer in the future.    There are plenty of mistakes
to make in distributed computing, and the chain of [they use generics, so I will use generics
differently and incorrectly elsewhere] seems less likely than other mistakes to me, and not
something which should be of particular concern to us.

More and more it seems there's not a techincal issue, just differing value judgments.   On
the positive side, we're beginning to understand each other, unless something sailed over
my head.

jamesG

-----Original Message-----
From: "Peter Firmstone" <jini@zeus.net.au>
Sent: Friday, December 17, 2010 7:02am
To: river-dev@incubator.apache.org
Subject: Re: Space/outrigger suggestions

jgrahn@simulexinc.com wrote:
> public Entry read(Entry template, Transaction txn, long timeout);
>
> That is indeed the original/current method's signature.
>
> A couple of points.
> 1) "The client knows the []'s class type, the class cast isn't much work" is an argument
against all generics, not just generics in this case.   


> It ignores the additonal specification power and type safety that the generic provides.

This is a very important comment, as it reflects the common 
understanding developers have of Generic's, this is true for code at 
compile time, however Java's generic implementation suffers from 
erasure, the type safety disappears after compilation and relies on the 
fact that the code has been audited by the compiler.

The binary signature of your method is the same as it is in bytecode 
now, adding generic parameters won't change the binary method signature.

It is unfortunate that your example will work with generic's, which is  
due to the template being passed as a parameter, restricting the return 
type to matching that of the template, the problem is, people don't 
understand why this works and just assume that generic's will work in 
all cases for distributed programming, then start using generics in 
their service api, it will work at first, but at some point in time, 
separately compiled code will be mixed, the class cast's won't have been 
checked by the compiler, then they'll get burnt by class cast 
exceptions, after deployment, the worst time to catch the problem, hence 
my comment, the type cast is simpler if performed by the client, where 
it can be checked at runtime.

The type casts weaved into bytecode by the compiler are checked at 
compile time, by the compiler and are not checked again at runtime.  
These checks are not performed in code that has been compiled separately.

Check out Jim Waldo's book:

http://www.amazon.com/Java-Good-Parts-Jim-Waldo/dp/0596803737

See also:

http://gafter.blogspot.com/2006/11/reified-generics-for-java.html

Cheers,

Peter.

>   It also discounts the work of adding the cast every time (mandatory boilerplate is
bad).
>
> 2) Returning "Entry" is what the method signature promises now, but it's not what the
space specification promises.   The read/take family of methods has a semantic gap between
what is contractually promised and what is checked by the compiler, and generics can close
that gap.
>
> ---
>
> I think I may still be missing something when it comes to your point about "separate
compilation".   In the case of using generics at the method level (again, not the class level),
the compiler not resolves on each method call, does it not?   How then would we get in trouble
with different compilation times?
>
> With the definition:
> public <T extends Entry>  T read(T template, Transaction txn, long timeout)
>
> Foo foo = space.read(someFoo,t,0); //Fine
> Bar bar = space.read(someBar,t,0); //Also fine
>
> Granted, if the space had previously seen some prior version of Foo, that's a runtime
kind of problem, but that's a runtime problem with or without the generic...
>
> Perhaps another example or a pointer to some resources to read would clarify this for
me.   (If you have the time.)
>
> jamesG
>
> -----Original Message-----
> From: "Peter Firmstone" <jini@zeus.net.au>
>
> The alternative method signature that is typesafe for James:
>
> public Entry read(Entry template, Transaction txn, long timeout);
>
> The client knows the template's class type, the class cast isn't much 
> work for the client developer.  Simpler is best I think, Generic's offer 
> no benefit for Service API.
>
> Hope this helps to clear it up.
>
> Cheers,
>
> Peter.
>
>
>
> Patricia Shanahan wrote:
>   
>> I'm working on a replacement FastList that assumes JDK1.5 or later, so 
>> that I can depend on the new memory model and some of the 
>> java.util.concurrent features.
>>
>> Do you advise using, or avoiding, generics in its definition?
>>
>> Patricia
>>
>>
>> On 12/14/2010 2:22 PM, Peter wrote:
>>     
>>> Generics are replaced with casts in bytecode.  All typesafe checks 
>>> are done at compile time and the generic replaced with a cast. If 
>>> clients are compiled separately, this check won't occur, and the cast 
>>> will be unchecked at runtime.
>>>
>>> If clients with identical bytecodes or type casts use javaspace it 
>>> will work, if separately compiled clients with different type casts 
>>> try to use the same space service, it will fail with class cast 
>>> exceptions at runtime.
>>>
>>> However since your T template is declared as a method parameter, the 
>>> javaspace service can check the class name at runtime and only return 
>>> that type.  This must be done in the javaspace service implementation.
>>>
>>> Only then will your generic method be typesafe.  So yes it will work, 
>>> but I want to make sure the complications of generics in separately 
>>> compiled code is well understood. It is not simple, but can be done 
>>> with due care.
>>>
>>> Users are going to have a hard time understanding how to implement 
>>> generics in their service implementations, it is fraught with 
>>> pitfalls that may not bite until after deployment.
>>>
>>> User devs expect generics to make life simpler, but it has the 
>>> opposite effect in remote code.
>>>
>>> We're either going to have to document the use of generics in service 
>>> api really well, or prohibit them.
>>>
>>> I think because it's possible it should be allowed, but we have to 
>>> document it well as an advanced feature that places the type check 
>>> burden on the service implementation.
>>>
>>> Cheers,
>>>
>>> Peter.
>>>
>>> ----- Original message -----
>>>       
>>>> Perhaps you could unpack your statement about generics for me a 
>>>> bit.    Are you
>>>> saying this wouldn't work?
>>>>
>>>> public<T extends Entry>  T read(T template, Transaction txn, long 
>>>> timeout)
>>>> (... with similar modifications to the other methods)
>>>>
>>>> The generic is defined at the method-level, enforcing that the type 
>>>> returned is
>>>> the type of the template (and that the template extends Entry).    
>>>> This is,
>>>> indeed, the current contractual obligation of the method.
>>>>
>>>> It would be unfortunate if we couldn't add this, because this would 
>>>> save our
>>>> users a cast every time they used JavaSpace, but there may be a 
>>>> technical hurdle
>>>> which I'm not understanding.
>>>>
>>>> Anyway, thought I'd attempt to clarify, since last time there was 
>>>> confusion over
>>>> whether I was asking for method-level generics or class-level 
>>>> generics (the
>>>> latter would break JavaSpace generally).
>>>>
>>>> jamesG
>>>>
>>>> -----Original Message-----
>>>> From: "Peter"<jini@zeus.net.au>
>>>> Sent: Tuesday, December 14, 2010 6:28am
>>>> To: river-dev@incubator.apache.org
>>>> Subject: Re: Space/outrigger suggestions
>>>>
>>>> I believe we can create jini community standards.
>>>>
>>>> If the service api is different, it is not breaking backward 
>>>> compatibility, it
>>>> is simply a different service.  A bridging service smart proxy can 
>>>> implement
>>>> javaspace and utilise the new service, allowing legacy clients to 
>>>> utilise the
>>>> new service.
>>>>
>>>> You could call it Balinda, Borne again Linda. ;)
>>>>
>>>> With generics and service api, compile time generic replacements 
>>>> must be the
>>>> same, otherwise a runtime class cast exception will occur.  This 
>>>> will work when
>>>> T is replaced by the same class, but will break when it isn't in 
>>>> separately
>>>> compiled code.  Generics that are specific will work.
>>>>
>>>> Cheers,
>>>>
>>>> Peter.
>>>>
>>>>
>>>>
>>>>
>>>> ----- Original message -----
>>>>         
>>>>> Who controls the JavaSpace API specification? Is it something we can
>>>>> change, as part of River, or do we just have an implementation?
>>>>>
>>>>> Should we be considering designing RiverSpaces, similar to JavaSpaces
>>>>> but with an updated API, including generics, more use of collections,
>>>>> and better naming?
>>>>>
>>>>> James - if you have time, could you file a Jira issue? That way, these
>>>>> ideas will not get lost in the mail archives.
>>>>>
>>>>> Patricia
>>>>>
>>>>> On 12/14/2010 12:33 AM, James Grahn wrote:
>>>>>           
>>>>>> I have a small list of suggestions for javaspace/outrigger, largely
>>>>>> derived from my experience creating a wrapper for space functionality
>>>>>> and direct usage prior to the creation of that wrapper.
>>>>>>
>>>>>> Many of these suggestions involve breaking backwards 
>>>>>> compatibility, so
>>>>>> many tears will be shed and perhaps we'll decide against implementing
>>>>>> any of these. But, I'm hoping this might lead to some discussion
and
>>>>>> perhaps some improvements.
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> 1) Generic methods.
>>>>>> First, use generics in the method signatures to minimize casting,
in
>>>>>> this manner:
>>>>>>
>>>>>> public<T extends Entry>  T read(T template, Transaction txn,
long 
>>>>>> timeout)
>>>>>>
>>>>>> Seems broadly like a win, if use of Java 1.5 idioms is acceptable.

>>>>>> This
>>>>>> is the only one I've mentioned before, and the reaction was fairly
>>>>>> positive on this list.
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> 2) More collection-like naming of space methods, more consistency.
>>>>>>
>>>>>> read, take, readIfExists, takeIfExists, write, snapshot, notify,
>>>>>> registerForAvailabilityEvent all have fine names. That is, they 
>>>>>> properly
>>>>>> describe the functionality and how the methods themselves relate

>>>>>> to one
>>>>>> another.
>>>>>>
>>>>>> I do, however, take issue with "contents", "take (with a 
>>>>>> collection)",
>>>>>> and "write (with a list)".
>>>>>>
>>>>>> I would suggest the following renamings:
>>>>>> contents ->  readAllExisting
>>>>>> take (with collection) ->  takeAny
>>>>>> write (with list) ->  writeAll
>>>>>>
>>>>>> This would eliminate the awkward overloading of "take" and "write"

>>>>>> while
>>>>>> bringing "contents" into a consistent naming plan.
>>>>>>
>>>>>> The goal is a naming scheme which clearly communicates functionality:
>>>>>> "exists/existing" suffix = nonblocking call
>>>>>> "any" suffix = one or more templates will be satisfied, multi-return
>>>>>> "all" suffix = all templates will be satisfied, multi-return
>>>>>> If unmodified, standard blocking call.
>>>>>>
>>>>>> The clearer naming also points to new functionality we could 
>>>>>> choose to
>>>>>> support, namely:
>>>>>> readAll - blocking call with all templates
>>>>>> readAny - blocking call on any template
>>>>>> takeAllExisting - nonblocking call with multiple templates.
>>>>>> takeAll - blocking call with all templates
>>>>>>
>>>>>>
>>>>>> Addendums:
>>>>>> 1) I'll admit that "any" is the weakest part of the syntax, as it

>>>>>> fails
>>>>>> to connote the multi-return. I was stretching to cover the current

>>>>>> "take
>>>>>> (with collection)" semantics, which blocks until at least one 
>>>>>> template
>>>>>> match is available. Open to better suggestions.
>>>>>>
>>>>>> 2) Though generally I dislike overloading methods, there is one 
>>>>>> case I'm
>>>>>> sympathetic to: overloading "all" and "allExisting" methods to 
>>>>>> take in a
>>>>>> single template or multiple templates. This would save some calls
to
>>>>>> Collections.singleton() for our users while maintaining a consistent
>>>>>> return type for the method.
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> 3) Collections or remote iterators, not both.
>>>>>> "contents" returns a remote iterator named "MatchSet", while "take

>>>>>> (with
>>>>>> collection)" returns a collection. I can understand the argument

>>>>>> behind
>>>>>> both use cases, but not necessarily the argument for using both
>>>>>> simultaneously.
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> 4) Exception soup.
>>>>>> Javaspace methods return a vast cornucopia of possible exceptions.
I
>>>>>> would propose wrapping any Exceptions bubbling up to River users

>>>>>> to be
>>>>>> wrapped in RiverException. Those few(?) who have special handlers
to
>>>>>> deal with problem conditions can peek into the cause.
>>>>>>
>>>>>>    From my observation, most libraries are either taking this 
>>>>>> route (ala
>>>>>> JAXB) or wrapping everything in runtime exceptions (Spring, IIRC).
>>>>>>
>>>>>> Presumably this suggestion could be applied to all of River, not
just
>>>>>> JavaSpaces.
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> 5) Clearer javadocs.
>>>>>> The current Javaspace docs are part protocol specification, part
>>>>>> implementation with some vital bits of information squirreled away
in
>>>>>> obscure reaches.
>>>>>>
>>>>>> For instance, in the 9 paragraphs describing the behavior of "take

>>>>>> (with
>>>>>> collection)":
>>>>>> "If there is at least one matching Entry available in the space,
an
>>>>>> invocation of this method must take at least one Entry. If more 
>>>>>> than one
>>>>>> matching Entry is available, the invocation may take additional 
>>>>>> entries.
>>>>>> It must not take more than maxEntries, but an implementation may

>>>>>> chose
>>>>>> to take fewer entries from the space than the maximum available or

>>>>>> the
>>>>>> maximum allowed by maxEntries."
>>>>>>
>>>>>> The above is a broad protocol specification to implementers (even
>>>>>> allowing that the method may always return an empty list ;-) ).
>>>>>> Frustrating to users because the definition is so amorphous.
>>>>>>
>>>>>> It also takes some doing to track down the fact that the 
>>>>>> implementation
>>>>>> does, in fact, limit the number of entries returned from a "take

>>>>>> (with
>>>>>> collection)". That tidbit is stored within the outrigger *package*
>>>>>> documentation, which reveals the setting and default (only 100).
>>>>>>
>>>>>>
>>>>>>
>>>>>> Aside: In prior discussion, I believe the reason for using that limit
>>>>>> was that the implementation creates an array of size Minimum(limit,
>>>>>> maxEntries)... and I think there's already a JIRA bug to switch 
>>>>>> from the
>>>>>> array to a collection. When we do, we should be a bit more 
>>>>>> generous with
>>>>>> the default (or remove the setting).
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> Anyway, hope this stirs some discussion.
>>>>>>
>>>>>> I'll be on vacation the rest of the month, so unfortunately my
>>>>>> participation in said discussion will likely be spotty (though 
>>>>>> I'll try
>>>>>> to look in). I've been meaning to push out these recommendations
for
>>>>>> some time, though, so I figured better now than waiting another 
>>>>>> month.
>>>>>>
>>>>>> jamesG
>>>>>>
>>>>>>             
>>>>
>>>>         
>>>       
>>     
>
>
>
>
>   




Mime
View raw message