--
You received this message because you are subscribed to the Google Groups "Dagger Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dagger-discus...@googlegroups.com.
To post to this group, send email to dagger-...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Can you give a more explicit example? Do you have a field like @Inject Set<? extends Foo> set, and a binding for Set<FooImpl>?
Dagger in fact considers those to be different keys. You are free to bind one to the other, for instance by using a @Binds method:
/** Satisfies requests for Set<? extends Foo> with Set<FooImpl>. */@Binds abstract Set<? extends Foo> bind(Set<FooImpl> set);
/home/raman/tmp/experiment-dagger2/src/main/java/experiment/dagger2/JavaDagger.java:40: error: java.util.Set<? extends experiment.dagger2.SetInstance> is bound multiple times:TestJ test();^@Binds Set<? extends experiment.dagger2.SetInstance> experiment.dagger2.TestModuleBinds.bindOne(Set<experiment.dagger2.SetInstanceOne>)@Binds Set<? extends experiment.dagger2.SetInstance> experiment.dagger2.TestModuleBinds.bindTwo(Set<experiment.dagger2.SetInstanceTwo>)
On Tuesday, October 4, 2016 at 10:13:47 AM UTC-4, David P. Baker wrote:Can you give a more explicit example? Do you have a field like @Inject Set<? extends Foo> set, and a binding for Set<FooImpl>?Yes, though I have multiple implementations of `Foo`, and in this case they are actually anonymous classes (lambdas).
Dagger in fact considers those to be different keys. You are free to bind one to the other, for instance by using a @Binds method:/** Satisfies requests for Set<? extends Foo> with Set<FooImpl>. */@Binds abstract Set<? extends Foo> bind(Set<FooImpl> set);Why would this be the case?In any case, this only works for one type of `Foo`, so its not a sufficient solution:
/home/raman/tmp/experiment-dagger2/src/main/java/experiment/dagger2/JavaDagger.java:40: error: java.util.Set<? extends experiment.dagger2.SetInstance> is bound multiple times:TestJ test();^@Binds Set<? extends experiment.dagger2.SetInstance> experiment.dagger2.TestModuleBinds.bindOne(Set<experiment.dagger2.SetInstanceOne>)@Binds Set<? extends experiment.dagger2.SetInstance> experiment.dagger2.TestModuleBinds.bindTwo(Set<experiment.dagger2.SetInstanceTwo>)I can make it work simply by declaring the injection site as (no @Binds required):@Inject Set<Foo> setbut as I said in my initial post, the type constraints on this set are not as tights as they could be -- at the injection site, given I only want (and should) read from this Set, `Set<? extends Foo>` is a more appropriate type signature than `Set<Foo>`.
On Thu, Oct 13, 2016 at 11:24 PM Raman Gupta <rocke...@gmail.com> wrote:I can make it work simply by declaring the injection site as (no @Binds required):@Inject Set<Foo> setbut as I said in my initial post, the type constraints on this set are not as tights as they could be -- at the injection site, given I only want (and should) read from this Set, `Set<? extends Foo>` is a more appropriate type signature than `Set<Foo>`.So you're saying that what Dagger currently does for multibindings works for you, but you want to know why it doesn't instead do use a wildcard element type for multibound sets?
I'm not sure whether doing that is worth requiring all injection points to add the "? extends" boilerplate.
The injected Set is unmodifiable. Dagger doesn't assume Guava is on the classpath, so we don't provide ImmutableSet.
Raman, I really do appreciate your ideas here. Let me respond to each:
- Doesn't that mean you'll never be able to bind @IntoSet Foo and @IntoSet Bar (which extends Foo) and get two different sets? (Set<Foo> and Set<Bar>)
- As I said before, I think the unavailability of add() is a mere side effect of the wildcard in Set<? extends Foo>, not its intended meaning.
- That conflict is by design.
- Multibinding isn't intended to be an advanced feature. It's a basic and very useful feature of Dagger. I wouldn't want to make that any harder for people to use.
I understand what you're asking for, but I think the current multibinding feature is what we want.
On Wednesday, October 19, 2016 at 10:21:18 AM UTC-4, David P. Baker wrote:Raman, I really do appreciate your ideas here. Let me respond to each:Thank you for saying this. Sometimes in these discussions it feels like one is just being annoying for no good reason :-) I'm glad you don't think so.
- Doesn't that mean you'll never be able to bind @IntoSet Foo and @IntoSet Bar (which extends Foo) and get two different sets? (Set<Foo> and Set<Bar>)
Without additional code to distinguish them, yes, that would be true. Wouldn't it be unusual to the point of likely being an error for a multibinding consumer to expect Set<Foo> and Set<Bar> to be bound separately?
It seems to me in this case, the user almost certainly intended to bind @IntoSet Bar as Foo, unless *all* of their @IntoSet bind Bar.
However, some extra code could support assignment to Set<? extends Foo> and Set<? extends Bar> to support this case as well.
- As I said before, I think the unavailability of add() is a mere side effect of the wildcard in Set<? extends Foo>, not its intended meaning.
I wouldn't say its a "mere" side effect. One could say its a "side effect", but its a side-effect that is *fundamental* to Set being covariant on `Foo`: the consumer of the Set does not know what the type of the objects in the Set are, so it simply cannot add to it.
- That conflict is by design.
That design is what we're discussing here, isn't it? :-)
- Multibinding isn't intended to be an advanced feature. It's a basic and very useful feature of Dagger. I wouldn't want to make that any harder for people to use.
I understand what you're asking for, but I think the current multibinding feature is what we want.Ok, fair enough. I can see both sides of the issue and certainly understand that the benefits (if any) of what I'm proposing aren't worth the effort required. Thanks for the discussion though!
On Wed, Oct 19, 2016 at 11:43 AM Raman Gupta <rocke...@gmail.com> wrote:On Wednesday, October 19, 2016 at 10:21:18 AM UTC-4, David P. Baker wrote:Raman, I really do appreciate your ideas here. Let me respond to each:
- Doesn't that mean you'll never be able to bind @IntoSet Foo and @IntoSet Bar (which extends Foo) and get two different sets? (Set<Foo> and Set<Bar>)
Without additional code to distinguish them, yes, that would be true. Wouldn't it be unusual to the point of likely being an error for a multibinding consumer to expect Set<Foo> and Set<Bar> to be bound separately?
Not in the least! Otherwise, won't everything just be Set<? extends Object>?
It seems to me in this case, the user almost certainly intended to bind @IntoSet Bar as Foo, unless *all* of their @IntoSet bind Bar.The return type of an @IntoSet is used exactly to specify which set to bind into. After all, I could have the same @Provides @IntoSet method return Bar, Foo, or Object. That's how I choose.
However, some extra code could support assignment to Set<? extends Foo> and Set<? extends Bar> to support this case as well.
- As I said before, I think the unavailability of add() is a mere side effect of the wildcard in Set<? extends Foo>, not its intended meaning.
I wouldn't say its a "mere" side effect. One could say its a "side effect", but its a side-effect that is *fundamental* to Set being covariant on `Foo`: the consumer of the Set does not know what the type of the objects in the Set are, so it simply cannot add to it.A hypothetical Set method could be addIfValid(Object), which would accept any object and add it only if it is a subtype of the accepted type of the Set. (That would require runtime type checking under the hood, of course.) That might not be a great idea, but it wouldn't fundamentally alter the notion of a typed Set. However, it would mean that Set<? extends Foo> is no longer necessarily unmodifiable. That's why I don't think the unmodifiability is a fundamental aspect of Set.
- That conflict is by design.
That design is what we're discussing here, isn't it? :-)Well, you started this thread by asking whether Dagger's considering Set<Foo> and Set<? extends Foo> different keys was a bug. I'm just saying it's not a bug—it's a feature. :)
- Multibinding isn't intended to be an advanced feature. It's a basic and very useful feature of Dagger. I wouldn't want to make that any harder for people to use.
I understand what you're asking for, but I think the current multibinding feature is what we want.Ok, fair enough. I can see both sides of the issue and certainly understand that the benefits (if any) of what I'm proposing aren't worth the effort required. Thanks for the discussion though!No worries; it's been helpful!For instance, one thing +Gregory Kick and I discussed as a result of this conversation was that the practice of injecting Provider<Foo> is not really technically correct (the best kind). You really want to inject a Provider<? extends Foo>. If everyone did that, the generated code might not have to suppress warnings when for instance it gives you a Provider<Bar> because of a @Binds. But unfortunately, Java's type system doesn't know that Provider<Bar> is a valid subtype of Provider<Foo>; hence the technical need to inject Provider<? extends Foo>.We decided that making everyone inject Provider<? extends Foo> was too onerous, since its only practical benefit would be to allow generated code not to suppress some warnings. However, we might allow people to inject Provider<? extends Foo> if they want to—which currently won't work. (We don't actually think anyone wants this enough for us to prioritize implementing it at the moment.)
Ok I had intended my last message to be my last word on the subject... but as usual I can't resist poking the bear a bit more :-)On Wednesday, October 19, 2016 at 12:52:08 PM UTC-4, David P. Baker wrote:On Wed, Oct 19, 2016 at 11:43 AM Raman Gupta <rocke...@gmail.com> wrote:On Wednesday, October 19, 2016 at 10:21:18 AM UTC-4, David P. Baker wrote:Raman, I really do appreciate your ideas here. Let me respond to each:
- Doesn't that mean you'll never be able to bind @IntoSet Foo and @IntoSet Bar (which extends Foo) and get two different sets? (Set<Foo> and Set<Bar>)
Without additional code to distinguish them, yes, that would be true. Wouldn't it be unusual to the point of likely being an error for a multibinding consumer to expect Set<Foo> and Set<Bar> to be bound separately?Not in the least! Otherwise, won't everything just be Set<? extends Object>?Ok. But isn't it just as easy to create injections for Set<? extends Foo> and Set<? extends Bar> as it is to create injections Set<Foo> and Set<Bar>, in the same way and for the same reasons?
It seems to me in this case, the user almost certainly intended to bind @IntoSet Bar as Foo, unless *all* of their @IntoSet bind Bar.The return type of an @IntoSet is used exactly to specify which set to bind into. After all, I could have the same @Provides @IntoSet method return Bar, Foo, or Object. That's how I choose.Yes I am well aware of that. Which is why I said that one *could* distinguish between these cases and inject into Set<? extends Foo>, and Set<? extends Bar> and (crazily) Set<? extends Object>. See point above.
On Wed, Oct 19, 2016 at 1:38 PM Raman Gupta <rocke...@gmail.com> wrote:Ok I had intended my last message to be my last word on the subject... but as usual I can't resist poking the bear a bit more :-)On Wednesday, October 19, 2016 at 12:52:08 PM UTC-4, David P. Baker wrote:On Wed, Oct 19, 2016 at 11:43 AM Raman Gupta <rocke...@gmail.com> wrote:On Wednesday, October 19, 2016 at 10:21:18 AM UTC-4, David P. Baker wrote:Raman, I really do appreciate your ideas here. Let me respond to each:
- Doesn't that mean you'll never be able to bind @IntoSet Foo and @IntoSet Bar (which extends Foo) and get two different sets? (Set<Foo> and Set<Bar>)
Without additional code to distinguish them, yes, that would be true. Wouldn't it be unusual to the point of likely being an error for a multibinding consumer to expect Set<Foo> and Set<Bar> to be bound separately?Not in the least! Otherwise, won't everything just be Set<? extends Object>?Ok. But isn't it just as easy to create injections for Set<? extends Foo> and Set<? extends Bar> as it is to create injections Set<Foo> and Set<Bar>, in the same way and for the same reasons?In that case, won't @IntoSet Bar be in both sets? As a user, how do I prevent that?
It seems to me in this case, the user almost certainly intended to bind @IntoSet Bar as Foo, unless *all* of their @IntoSet bind Bar.The return type of an @IntoSet is used exactly to specify which set to bind into. After all, I could have the same @Provides @IntoSet method return Bar, Foo, or Object. That's how I choose.Yes I am well aware of that. Which is why I said that one *could* distinguish between these cases and inject into Set<? extends Foo>, and Set<? extends Bar> and (crazily) Set<? extends Object>. See point above.OK, maybe I'm not understanding you. I thought you wanted @IntoSet Bar to contribute to Set<? extends Bar> as well as Set<? extends Foo>, etc. If not, then fine.
I've been watching this tread, and now that it's settled down a bit I figured I'd chime in with a few thoughts.
1) For injection all types are treated as invariant. No notion of assignability is ever considered when performing injection for sets or any other type. In order for Foo to be injected, Foo must be bound. There are no plans to change that generally or for any particular subset of bindings.
2) While it is technically true that we could bind sets with '? extends', in practice all sets of our sets are actually Set<Foo>. Due to the invariance in binding, all @IntoSet bindings are bound as Foo and all @ElementsIntoSet bindings are Set<Foo>. Even sets contributed with @Binds need a defensive copy, so those are also Set<Foo>. So, the ability to request a Set of something that extends Foo is fine enough, but only matters in theory -- in all cases that you might be able to accept something else at your injection site, you're only going to get an instance declared as Set<Foo> anyway.
3) That said, there's no reason to preclude binding Set<? extends Foo>, but IMO @Binds is the appropriate way to do that:@Binds Set<? extends Foo> bind(Set<Foo> set);
4) I think the assertion has been made that somehow adding a wildcard precludes the use of the modification operations. This is not the case because wildcard capture can make that work for add(). In fact, that is exactly the example in https://docs.oracle.com/javase/tutorial/java/generics/capture.html . Other modification methods like remove accept Object, so the type variable doesn't make any difference at all. Adding a wildcard does little more that make a particular form of modification more difficult, but doesn't actually protect against anything.Hope that clarifies some of the thinking here.
Dagger2 is unable to inject a `Set<T>` into a covariant `Set<? extends T>` field. Is there any particular reason for this, or could this be considered a bug?As context: I found this while prototyping Dagger2 with Kotlin, in which `Set<T>` is read-only and can therefore accept covariant types. One has to fall-back to declaring the injection site as a `MutableSet<T>` to get the invariant type, even if the `Set` is intended to be read-only.
To make Kotlin APIs work in Java we generate Box<Super> as Box<? extends Super> for covariantly defined Box (or Foo<? super Bar> for contravariantly defined Foo) when it appears as a parameter. When it's a return value, we don't generate wildcards, because otherwise Java clients will have to deal with them (and it's against the common Java coding style). -- Source
@Module
class SimpleModuleA {
@Provides
@Singleton
@IntoSet
fun provideDependency11() = Dependency11()
}
@Module
class SimpleModuleB {
@Provides
@Singleton
@ElementsIntoSet
fun provideDependencies13(): Set<Dependency1> {
val dependencies = mutableSetOf<Dependency1>()
for (i in 1..2) {
dependencies.add(Dependency13())
}
return dependencies
}
}
@Injector
lateinit var dependencies: Set<Dependency1>
@Singleton
class Dependency2 @Inject constructor(dep1s: Set<Dependency1>)