square / javapoet

A Java API for generating .java source files.
Apache License 2.0
10.82k stars 1.38k forks source link

Version 3.0 Working Spec #51

Closed JakeWharton closed 9 years ago

JakeWharton commented 10 years ago

This issue will serve as a working space for what a potential version 3.0 would look like with a new, more dynamic, and more powerful API.

High-Level Goals

Currently, type compression via imports requires knowledge of all types up before the body of the file. In practice, this usually requires an awkward two-pass approach where you determine the imports based on a pass of dynamic contents and then emit the body based on a second pass.

The goal of this is to always emit fully-qualified types from the API and rely on a post-compression phase for emitting imports.

There are multiple ways to approach this. A non-exhaustive list:

Objects which represents pieces of code lend themselves naturally to writing emission code in Java. The wins should hopefully be fairly obvious.

There is a potential for API explosion here to cover the diversity of the Java language. A potential also exists for adding an unacceptable amount of boilerplate to the API.

cgruber commented 10 years ago

To bring code-blocks into this context, here's the comment from the other thread (with re-phrasing):

Greg kick was toying with an idea (and I expended it a bit) of emitting a sort of code-block, such that instead of building up code, you have code that looks a lot (structurally) like the code you're going to emit, using the block initializer jazz. Very much the way Spec/BDD frameworks do. Sort of like:

    CompliationUnit block = new CompliationUnit("com.google.whatever") {{
     add(new ClassBlock(EnumSet.of(PUBLIC, FINAL), "MyClass") {{
       add(new MethodBlock(EnumSet.of(PUBLIC, STATIC), Void.class, "main") {{
         // You can loop.
         for (Foo foo : foos) {
           // Short cut for quick-and-dirty simple statements. 
           addStatement("%s.out.println(\"Hello World\")", System.class);         
         }
         // More complex statement chains. 
         identifierFor(System.class).dot("out").dot("println").invoke(literal("Hello World!"));
       }});
     }});
    }};

It's totally a straw man, but this approach results in code that in shape/indentation/layout strongly matches the resulting emitted code, lets us do all the right things with accumulating types so we can emit shortened versions, etc. Heck, it can reflectively validate stuff along the way.

It can also be composed.

    ClassBlock[] blocks = new ClassBlock[someNumber]
    for (int i = 0; i < someNumber; i++) {
      blocks[i] = new ClassBlock(EnumSet.of(PUBLIC, FINAL), "MyClass" + i) {{
        add(new MethodBlock(EnumSet.of(PUBLIC, STATIC), Void.class, "main") {{
          identifierFor(System.class).dot("out").dot("println").invoke(literal("Hello World!"));
        }});
      }};
    }
    CompliationUnit block = new CompliationUnit("com.google.whatever") {{
     addAll(asList(blocks));
    }};

There are some down-sides, and the names suck just now, but there are some handy aspects to it. I normally really like fluent interfaces, but in this case, I think it doesn't serve the readability.

JakeWharton commented 10 years ago

Here's my notes from what we talked about. Placing here for archive, reference, and the public to comment on.


gk5885 commented 10 years ago

I tried my very best to destroy it all with horrible misuse of git rebase, but here's my initial prototype of what JavaWriter 3.0 might look like: https://github.com/gk5885/javawriter/tree/javawriter-3 I included a few notes about what it's trying to do and what hasn't been implemented yet: https://github.com/gk5885/javawriter/commit/204a0208d41b0e345cd1072cb3d6d38d61d9849d

PTAL

JakeWharton commented 10 years ago

Awesome. Will look tonight or in the morning.

JakeWharton commented 10 years ago

@gk5885 Any updates? Anything we can help with?

gk5885 commented 10 years ago

What I ended up doing was taking the strawman and dumped it into dagger so that I could actually try to write something real with it. Right now Dagger 2 is implemented entirely using this version of the API and the generators were just about cut in half. Right now I'm running it against all of our existing Dagger usages to squash some bugs and fill out the implementation.

Within the next few weeks I want to give the whole API another look and get everybody together to pick some better names and interaction patterns. I'll be sure to report back once it's all more fleshed out.

On Tue Jul 01 2014 at 6:47:11 PM, Jake Wharton notifications@github.com wrote:

@gk5885 https://github.com/gk5885 Any updates? Anything we can help with?

— Reply to this email directly or view it on GitHub https://github.com/square/javawriter/issues/51#issuecomment-47729104.

JakeWharton commented 10 years ago

Sounds good. I saw the Dagger PR come in this morning. I've been tinkering with the new API myself in small things but I have a large thing that I'm eager to drop it in.

cgruber commented 10 years ago

Ok - a lot of this has been refined under design pressure from dagger2. I want to look at pulling it out at some point soonish, but how we go about it is probably worth chatting about. Should we fork in google and push it out that way? We probably want a google fork, so we can vary it as quickly as our internal changes demand.

cgruber commented 10 years ago

(wrong button)

JakeWharton commented 10 years ago

Yes, please. I have three projects absolutely dying to use the new API and I've been resisting creating four working copies by duplication!

cgruber commented 10 years ago

Heh. Yeah - I want to convert autofactory over to it pronto, plus some other things.

JakeWharton commented 10 years ago

Let me know if there's anything I can do to help.

cgruber commented 10 years ago

I'm going to have to make it a google groupid in our fork, I think, as I will need to be able to drop snapshots to sonatype quickly. I think we're going to be iterating on it still some. Greg's off today so I'll talk to him about this tomorrow. It's not the highest priority, but I'd like to move it out soon, so we can cross-use it without having other projects take on a dagger dependency.

gk5885 commented 10 years ago

While I understand the desire to have the new version out and while the implementation is pretty solid, the API is just abysmal right now. I'm -1 on having anybody use this until we get it reviewed and cleaned up because as it stands right now, the migration from JW3 to JW3.1 would be just as bad as JW2 to JW3.

cgruber commented 10 years ago

Fair enough.

swankjesse commented 9 years ago

I'm closing this working spec issue.

Deferred type compression is done.

Code block objects are here: https://github.com/square/javawriter/issues/171