The Source for Java Technology Collaboration
User: Password:



David Walend

David Walend's Blog

What Giants? - Vote For My Generics RFE

Posted by dwalend on July 18, 2006 at 05:30 AM | Comments (14)

Back in May I blogged about simplifying my generics code with dot accessors to the type parameters, to simplify code that currently looks like this:


public class FloydWarshall<Node,
                            Edge,
                            Label,
                            BaseDigraph extends IndexedDigraph<Node,Edge>,
                            LabelDigraph extends IndexedMutableOverlayDigraph<Node,Label,Edge,BaseDigraph>,
                            SRing extends Semiring<Node,Edge,Label,BaseDigraph,LabelDigraph>>

into something more like this:


public class FloydWarshall<SRing extends Semiring>

The request for enhancement made it into Sun's database. That whooshing sound may not be a windmill after all.

If you can spare a bug vote, please vote for this RFE. Judging by the evaluation from Sun's engineer, this RFE needs some votes raising it up so the Sun language giants might spot it.

The RFE will also need some good rational discussion. I held back my irrelevant knee-jerk reaction -- "Didn't we all out-grow one-letter-variables when we traded our PETs for C-64s?" I could send a link to an old blog, but even that might distract from the cause. Please keep in mind that we want these folks to do us a favor. I'm working on a response that frames the RFE as "encapsulation vs. exposure," to dispel the "inference vs. explicitness" suggestion.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • The reviewer's evaluation of your proposal is illuminating:

    "Regarding the suggested language change, don't get your hopes up. Explicit declarations are favored in the Java programming language as it generally makes code easier to read (at the expense of writing it)."

    I almost laughed out loud at the "it generally makes code easier to read" part of this comment... I can't imagine how anyone would thing so.

    -JohnR

    Posted by: johnreynolds on July 18, 2006 at 06:19 AM

  • Thanks, John. My first thoughts were along the same lines. My guess is the reviewer scans a hundred or so RFEs per day, sorted by category, and that he just finished a few "type inference like in P-languages" requests. He's probably quoted that party line in a dozen RFEs today. In general, I think Sun's hit the right spot with explicit declarations. I just want some encapsulation and reuse of them. We should cut him some slack. He's helping the giants we want help from. The suggested fix was what really got me smirking. I had a geospatial rig open on another monitor. N E and S in the comments primed me to look for W, D and U.

    Posted by: dwalend on July 18, 2006 at 07:32 AM

  • A while back I wrote a graph package and used something like:
    import java.util.*;
    
    // HasState is possible to code as you want
    interface HasState< HS extends HasState< HS > > {
        Class< ? extends HS > getPrincipleInterface();
    }
    
    // Now tying Digraph etc. to a given family of Node and Edge types
    // The key is to use Node and Edge as generic parameters throughout
    // IE every type has generic parameters < N extends Node< N, E >, E extends Edge< N, E > >
    interface Node< N extends Node< N, E >, E extends Edge< N, E > > {
        Set< E > getEdges();
        void addEdge( E e );
    }
    
    interface Edge< N extends Node< N, E >, E extends Edge< N, E > > {
        N getStart();
        N getEnd();
    }
    
    interface Digraph< N extends Node< N, E >, E extends Edge< N, E > > {
        Set getEdges();
        Set getNodes();
    }
    
    interface Subgraph< N extends Node< N, E >, E extends Edge< N, E > > extends Digraph< N, E > {
        Supergraph< N, E > getSupergraph();
    }
    
    interface Supergraph< N extends Node< N, E >, E extends Edge< N, E > > extends Digraph< N, E > {
        Set< Subgraph< N, E > > getSubgraphs();
    }
    
    // Now test them
    // Family A classes
    class NA implements Node< NA, EA > { // Base Node type in family A
        private Set< EA > edges = new HashSet< EA >();
        public final Set< EA > getEdges() { return edges; }
        public final void addEdge( EA e ) { edges.add( e ); }
    }
    
    class NA2 extends NA {} // 2nd Node type in family A
    
    class EA implements Edge< NA, EA > { // First Edge type in family A
        private final NA start;
        private final NA end;
        EA( final NA start, final NA end ) {
            this.start = start;
            start.addEdge( this );
            this.end = end;
            end.addEdge( this );
        }
        public final NA getStart() { return start; }
        public final NA getEnd() { return end; }
        public final String toString() { return start + " -> " + end; }
    }
    
    abstract class DA implements Supergraph< NA, EA > {
        private final Set< EA > edges;
        DA( Set< EA > edges ) { this.edges = edges; }
        public final Set< EA > getEdges() { return edges; }
        public final Set< NA > getNodes() {
            final Set< NA > nodes = new HashSet< NA >();
            for ( final EA edge : edges ) {
                nodes.add( edge.getStart() );
                nodes.add( edge.getEnd() );
            }
            return nodes;
        }
        public final String toString() {
            final StringBuilder result = new StringBuilder();
            for ( final EA edge : edges ) {
                result.append( edge );
                result.append( '\n' );
            }
            return result.toString();
        }
    }
    
    class SuperA extends DA implements Supergraph< NA, EA > {
        SuperA( final Set< EA > edges ) { super( edges ); }
        public final Set< Subgraph< NA, EA > > getSubgraphs() { // very much an example!
            final Set< EA > subEdges = new HashSet< EA >();
            subEdges.add( getEdges().iterator().next() );
            final Set< Subgraph< NA, EA > > subgraph = new HashSet< Subgraph< NA, EA > >();
            subgraph.add( new SubA( this, subEdges ) );
            return subgraph;
        }
    }
    
    class SubA extends SuperA implements Subgraph< NA, EA > {
        private final SuperA zuper;
        SubA( final SuperA zuper, final Set< EA > edges ) {
            super( edges );
            this.zuper = zuper;
        }
        public final SuperA getSupergraph() { return zuper; }
    }
    
    // Family B class
    //class NB implements Node< NB, EA > { // Base Node type in family B - compiler catches error of using EA
    //    private Set< EA > edges = new HashSet();
    //    public final Set< EA > getEdges() { return edges; }
    //    public final void addEdge( EA e ) { edges.add( e ); }
    //}
    
    public class Main {
        public static void main( final String[] notUsed ) {
            final NA na = new NA();
            final NA na2 = new NA2();
            final Set< EA > edges = new HashSet< EA >();
            edges.add( new EA( na, na2 ) );
            final SuperA graph = new SuperA( edges );
            System.out.print( graph.getSubgraphs() );
        }
    }
    
    I didn't find I needed any particularly complicated generic definitions.

    I do however agree that the syntax of generics is ugly, it makes html posts (like the above) a pain, and that perhaps they went too far with the variance stuff; i.e. I have never actually had an ArrayStoreException.

    Posted by: hlovatt on July 18, 2006 at 10:48 PM

  • Hi hlovatt,

    You're right. Generic Digraph types themselves aren't really any worse than Maps.

    I had to resort to using all the exposed declarative complexity when I started building generic algorithms on top of the generic Digraphs. Semiring algorithms (shortest path and its kin) really demonstrate the problem because the problem's input is Digraph<Node,Edge> and the general output is Digraph<Node,Digraph<Node,Edge>>.

    You're not kdding about html format for generics posts. What a time sink!

    Posted by: dwalend on July 19, 2006 at 05:39 AM

  • Sorry, it's closed; will not fix.

    Posted by: erickson on July 19, 2006 at 11:42 AM

  • I don't think it is fair to sugges that the reviewer who denied your request didn't pay more than passing attention to your request.

    For starters, he reformulated your code. Now unless he got it wrong (of which I can find no indication from you or others) then he had to have at an absolute minimum read your request in detail.

    Secondly, you can see that he went and ran it in the latest build of Mustang (and didn't get the error you reported), so he's even gone to the trouble of taking the code and going off and compiling it.

    Thirdly, the readability issue. Whilst I personally think that Generics are a crime against nature and despise and loathe them with a passion, readability is a bit of a personal thing, and I personally found that the reviewers code was a little bit more readable. (Which since I think generics are horrible and unreadable amounts to little more than saying his raw sewage is slightly tastier than your raw sewage - perhaps in time I shall learn to muscle my way past the gag reflex, but I doubt it ;-)

    The problem with Generics is that there is no win. It is supposed to 'save us' from the terrible fate of typecasting (oh noes, oh woes is us etc.) However, all that happens is that it moves the (Foo) from in front of the variable, and turns it into and shoves it on the end of the type. And what is worse, most of the example code has more typing than there would be casting, so it is a net loss. (And, in general, darn hard to read for us old timers)

    I wonder when these Earthlings will discover the superior technology of vinyl^H^H^H^H^H Interfaces

    Posted by: rickcarson on July 19, 2006 at 04:27 PM

  • Ah, yes I see the problem with shoving example generics in html. My goodness that is annoying. I wonder if using lt and gt will work?

    Post should read... turns (Foo) into <Foo> for no gain whatsoever.

    Well, preview, (the lying trollop that it is) says that works, so lets give it a go...

    Posted by: rickcarson on July 19, 2006 at 04:30 PM

  • Following on from example above, I think I can see what you are doing. We took a different approach, for stuff like:

    Digraph< Node, Digraph< Node, Edge > >
    

    We used the composite pattern, so the return type would be:

    Digraph< N, E >
    

    But the E returned would implement something like:

    interface Composite< N extends Node< N, E >, E extends Node< N, E > > {
        Diagraph< N, E > getDiagraph();
    }
    

    as well as Edge. That way it can primarily be treated like an Edge but if you need to you can get at the underlying graph.

    It worked out OK for us :)

    Posted by: hlovatt on July 20, 2006 at 01:37 AM

  • hlovatt, that extra layer of indirection looks interesting...

    How would it hold up for something like FloydWarshall? Do you see a way to push it into Semiring?

    The real power we should be able to get out of generics is generic algorithms like FloydWarshall, Dijkstra , and AStar. There are a lot of different types flying around, all incestuously related to each other. I've managed to shove all the complexity into the type parameters. Now I'd like encapsulation to manage that complexity for me.

    Posted by: dwalend on July 20, 2006 at 05:09 AM

  • erickson,

    Wow. Three months to get it in the bug parade, three days to deny it. I am making Don Quixote allusions for a reason.

    Someone mentioned an appeal process at JavaOne. I'll put together my comment first, then try that path.

    Posted by: dwalend on July 20, 2006 at 05:18 AM

  • rickcarson,

    I do not agree with your statement that generics shift only the cast. I see two implications here, I found to be important:

    1. I am working within a big project. Therefore I am using a lot of code written by other people. The projects are pressurizing and the javadoc's are lousy sometimes. So I prefer to see something like Map<String, String> in a signature instead of Map only.
    2. For the same reason, I prefer breaking someones code at build time and not at runtime. In my environment, integration cost are extremely high therefore I'd rather break the build.

    Posted by: rbirenheide on July 21, 2006 at 07:15 AM

  • RE: Composite (extra layer of indirection)

    The graph project I was involved in didn't use any standard graph reduction algorithms; one of the reasons for this was that the reduction involved the properties of nodes, not edges, and the second reason was that the reduction was very application specific. Therefore I can't comment from direct experiance, infact I am not even sure if I remember what a semiring is! I think it might be some type with: multiply, add, zero, and one methods! Assuming my memory is correct, then I would define a Semiring interface:

    Semiring< N extends Node< N, E >, E extends Edge< N, E > > {
      Semiring< N, E > multiply( Semiring< N, E > rhs );
      Semiring< N, E > add( Semiring< N, E > rhs );
      Semiring< N, E > zero();
      Semiring< N, E > one();
    }
    

    Then I would make Edge extend Semiring.

    Warning: Blatent Plug For Own Pet Project :)

    There is a problem with binary numeric type operations and single dispatch; in that you really want multiple dispatch, that is why you need to type test in an equals method (typically an instanceof test is used). This point about numerics really requiring multiple dispatch is expanded more here. Multiple dispatch can be added to Java using my extended compiler. The compiler I wrote enforces design patterns, it is called a pettern enforcing compiler or PEC, and one of the patterns is Multiple Dispatch.

    Posted by: hlovatt on July 21, 2006 at 07:52 PM

  • Thanks for the pointer, hlovatt. I'll follow up on that one. They might be giants.

    Posted by: dwalend on August 10, 2006 at 07:40 PM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds