dagger icon indicating copy to clipboard operation
dagger copied to clipboard

Graph visualization doesn't display Lazy bindings well.

Open jbeder opened this issue 12 years ago • 6 comments

The following module:

@Module(library = true)
class LazyModule {
  @Provides Foo provideFoo() {
    return new Foo();
  }

  @Provides Bar provideBar(Lazy<Foo> foo) {
    return new Bar();
  }

  @Provides Baz provideBaz(Foo foo) {
    return new Baz();
  }
}

produces the .dot file:

digraph G1 {
  concentrate = true;
  n2 [label="Bar"];
  n3 [label="Lazy<Foo>"];
  n2 -> n3;
  n4 [label="Baz"];
  n5 [label="Foo"];
  n4 -> n5;
}

which doesn't show any relationship between Foo and Lazy<Foo>. I'd expect either

(a) there should be an arrow from Foo -> Lazy<Foo>, indicating that to produce a Foo, you need a Lazy<Foo>; or

(b) Foo and Lazy<Foo> should be the same node.

jbeder avatar Nov 01 '13 22:11 jbeder

Hmm. Other way around - Lazy<Foo> requires Foo. Or rather, technically, it requires Provider<Foo>. But from a sort of structural dependency, Lazy<Foo> is a wrapper of foo, and providers are all implicit, so I"m not sure how to best represent that in the digraph.

It might be reasonable that if Lazy<Foo> and Foo both occur in the graph, we can add Provider<Foo> and have them both point at that. But I'm not sure. Is it not clear what the graph intention is?

The simplest way I can see is to simply have Lazy<Foo> point at Foo, because by the point where you get() on Lazy<T> it will either HAVE created or WILL create Foo before get() returns. That suggests to me that Lazy<Foo> -> Foo.

Another option woudl be that Lazy<Foo> isn't a node, but we special case it, and it becomes a specialized edge in the digraph - where we link Baz -----> Foo and Bar ---lazy--> Foo (or something along those lines)

cgruber avatar Nov 01 '13 23:11 cgruber

I see what you mean - I think you could make an argument for either direction for the arrow; I was thinking of the lazy.get() call as fake method that looks like

Foo provideTheRealFoo(Lazy<Foo> lazy) { ... }

(I sorta view all providers, e.g. from T to U, as maps from Lazy<T> to Lazy<U>, which leads me to this viewpoint.)

But you're right, it could be viewed the other way, and they both naturally depend on a Provider.

Given that there are multiple ways of thinking about this, I like the idea of having a specialized "lazy" edge.

jbeder avatar Nov 01 '13 23:11 jbeder

Oh no.

@Provide Foo provideFoo(Lazy<Foo> lazy) { }

That should break, hard. That is a circular dependency, specifically, and should fail at compile time. If it doesn't, we need to file a bug. I think there is no plausible way to think of it as Foo -> Lazy<Foo> given what lazy is... it's simply a lazy creator. You're depending on Foo... but only actually demanding it when you use it.

Anyway, I'll play with digraphs manually and see if I can come up with something visually useful.

cgruber avatar Nov 01 '13 23:11 cgruber

Heh, no, I didn't mean as a @Provides method, just a way to think about dependencies.

You can think about Lazy<T> as a monad, with provider methods looking like T -> Lazy<U>; you can ask gak about all the horrible ways I've been trying to use Dagger :)

Thanks for taking a look!

jbeder avatar Nov 01 '13 23:11 jbeder

So how about something like this:

digraph G1 {
  concentrate = false;
  n2 [label="A"];
  n3 [label="B"];
  n4 [label="C"];
  n5 [label="D"];
  n6 [label="E"];
  n7 [label="F"];
  n8 [label="G"];
  n2 -> n3 [fontsize="10", style="dashed", fontcolor="goldenrod", color="goldenrod" label="<lazy>"];
  n4 -> n3 [fontsize="10", style="dashed", fontcolor="goldenrod", color="goldenrod" label="<lazy>"];
  n5 -> n2 [fontsize="10", style="dotted", fontcolor="blueviolet", color="blueviolet" label="<provider>"];
  n4 -> n5;
  n4 -> n2 [fontsize="10", style="dotted", fontcolor="blueviolet", color="blueviolet" label="<provider>"];
  n6 -> n3 [fontsize="10", style="dashed", fontcolor="goldenrod", color="goldenrod" label="<lazy>"];
  n8 -> n7;
  n8 -> n2 [fontsize="10", style="dotted", fontcolor="blueviolet", color="blueviolet" label="<provider>"];
  n7 -> n3 [fontsize="10", style="dashed", fontcolor="goldenrod", color="goldenrod" label="<lazy>"];
  n4 -> n6;
}

Note - concentrate = false results in slightly better lines in larger graphs. Otherwise, they end up merging where they shouldn't, like a normal dependency laying over a lazy/provided dependency, so it's obscured.

I separated provider and lazy by both colour and line style, as well as adding a little label.

cgruber avatar Nov 03 '13 18:11 cgruber

I like it!

jbeder avatar Nov 03 '13 19:11 jbeder