Proposal: Optional<T>

Coordinator
Feb 22, 2010 at 4:20 AM
Edited Feb 22, 2010 at 4:21 AM

One of the other types of relationships a component can have with one of it's dependency is an optional one. In these cases the component explicitly is stating that it can survive without it's dependency. Many containers today do support optional dependencies through the use of properties. For example Windsor assumes property dependencies are optional. MEF (as an example) on the other hand assumes property dependencies are required by default.

There are situations where assuming property dependencies are optional is a bad assumption. In particular when you have an existing component instance whose lifetime the container does own (ie the container did not create it) and which you would like to inject dependencies into after it is created. A canonical case would be a user control created by the XAML parser. In these cases using constructor injection is not an option, thus property injection (or less common method injection) is the only option making it invalid to assume just because it is a property it is default.

Taking this even further you can imagine a dependency on a constructor might actually be optional, meaning if the service is not available leave it to it's default value. Today this is difficult to represent as the container assumes if it sees a constructor parameter, it must provide a value or it will be an error.

The proposal is to introduce Optional<T> as a standard way to identify a dependency as optional. Optional<T> could easily be supported on both parameters and constructors.

Design:

public class Optional<T> (

 public Optional(T value) {
 }

 public Optional() {
 } 

 public T Value {get;set;}
)

The behavior of Optional<T> is the following:

  • If a dependency is an Optional<T> and T is registered in the container, then Optional<T> will be constructed using the value overload passing the T registered in the container.
  • If a dependency is an Optional<T> and T is not registered in the container, then an Optional<T> will be constructed using the default ctor which will set Value to default(T)

Thoughts? 

Feb 22, 2010 at 6:26 AM

Glenn, it would be great to have a mechanism by which one can be notified if an optional dependency becomes available/unavailable at runtime (for recomposition scenarios).
Please consider adding a HasValue bool and an AvailabilityChanged event.

Also, shouldn't the set property of Value be private?

Cheers,
Shy.

Coordinator
Feb 22, 2010 at 9:42 AM
Edited Feb 22, 2010 at 9:42 AM

I like this, even though it challenges the fairly widely-accepted "constructor parameters = mandatory, properties optional" convention.

The big drawback with properties-as-optional is the need to make the underlying value mutable. Despite a dependency being optional, it is usually cleaner to do all initialisation in the constructor and use readonly backing fields.

Using Autofac I'd tend to work around this same scenario using multiple constructors - one excluding the optional dependency - but I know this isn't portable.

Optional<T> might therefore be a good adapter to have on the table.

Shy, for AvailabilityChanged I think you are looking for something like Recomposable<T> (if in fact there's a way to represent it, have never given it any thought.. :)) Keeping to one concern per adapter means that containers can selectively exclude support for features they don't have.

HasValue is a nice addition.

Nick

Feb 22, 2010 at 10:05 AM

I think this proposal introduces a formal way to declare a Leaky Abstraction - something that I'm not particularly fond of.

I don't buy the example concerning User Controls, as I consider it a design smell to have User Controls with dependencies. Controls should be dumb Views with only a little (if any) self-contained behavior.

We can model optional dependencies just fine without any special type signalling this. I prefer using Property Injection for optional dependencies, but overloaded constructors are another prosibility. An optional dependendency always implies some kind of default implementation, either in the form of a proper default implementation, or alternatively in the form of a Null Object. When a dependency is truly optional, it is the dependent's responsibility to instantiate the default dependency until/unless a different implementation is supplied from the outside.

If we want to ensure that the dependent guarantees its invariants, we can use a combination of a readonly backing field combined with Constructor Injection. In that case we would have more than an overloaded constructor because one constructor would call into another by creating a new instance of the default implementation.

If this is not necessary, we can expose the optional dependency as a writable property, but the dependent must still provide the default implementation to itself in the case where it isn't being externally assigned.

In both cases, this gives a DI Container a proper way to opt out of the optional dependency because it doesn't have to serve an instance of the optional dependency. However, if it has a configuration for the optional dependency, it can still serve it.

Coordinator
Feb 22, 2010 at 11:10 AM

Mark, I don't see how this leaks any information that isn't also implied by the property-injection or multiple-ctor solution, but there's a measure of taste involved. Its a trade-off between minimalism and explicit expression of intent, perhaps?

We're targeting portability though, not trying to define best practice, so from that perspective the issues are different.

Existing conventions might struggle here because they can't be as easily standardised. The horse has already bolted on constructor selection, for example - despite our shared preference for most-available-dependencies-wins, several popular containers use other conventions that fit better with their models.

An adapter type on the other hand can have whatever semantics we desire applied to it.

You've highlighted our implicit assumption of a single public constructor as the only dependency declaration. This might be worth examining more closely, and I'm interested to hear other thoughts on it. If the multiple-ctor or property approach can be made to work in a portable way then that would naturally be a compelling alternative to something like Optional.

Feb 22, 2010 at 11:45 AM
Edited Feb 22, 2010 at 11:47 AM

I must say I agree with Mark on this.

If a dependency is optional I think the Right Thing™ to do is to be explicit about it not by throwing some infrastructure level indirection layer, but do it via ctor overloads or settable property.

By leaking abstraction I think Mark means difference between:

 

public class Foo

{

public IBar Bar {get ;set; }

}

 

and

 

public class Foo

{

public IOptional<IBar> Bar {get ;set; }

}
 

 

It is Foo's internal affair whether or not it requires a valid instance of IBar to work or not - it shouldn't announce that to the entire world like this.

Also even if you want to inject mandatory dependency as property after component is created - I think all containers have this ability so it's not really an issue.

Developer
Feb 22, 2010 at 1:50 PM
I agree with this too. 
Use ctor if it's required, use property setter if it's optional.

If you're using C# 4, it would be nice if optional defaulted arguments indicated optional dependencies in the constructor. i.e.:

public Foo(IBar bar /* required */, IBaz baz = null) { }

/kzu

--
Daniel Cazzulino | Developer Lead | XML MVP | Clarius Consulting | +1 425.329.3471


On Mon, Feb 22, 2010 at 09:46, kkozmic <notifications@codeplex.com> wrote:

From: kkozmic

I must say I agree with Mark on this.

If a dependency is optional I think the Right Thing™ to do is to be explicit about it not by throwing some infrastructure level indirection layer, but do it via ctor overloads or settable property.

By leaking abstraction I think Mark means difference between:

public class Foo

{

public IBar Bar {get ;set; }

}

and

public class Foo

{

public IOptional<IBar> Bar {get ;set; }

}
 

It is Foo's internal affair whether or not it requires a valid instance of IBar to work or not - it shouldn't announce that to the entire world like this.

Also even if you want to inject dependency as property after component is created - I think all containers have this ability so it's not really an issue.

Read the full discussion online.

To add a post to this discussion, reply to this email (cca@discussions.codeplex.com)

To start a new discussion for this project, email cca@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Feb 22, 2010 at 7:30 PM

Krzysztof is pretty close, but perhaps I chose my terminology without thinking it through: it's not so much a Leaky Abstraction as a Redundant Abstraction.

Let's take a step back and forget all about DI Containers for a minute. If we consult the Framework Design Guidelines, we can read the following about properties:

  • Do provide sensible default values for all properties [...]
  • Do allow properties to be set in any order [...]

Allowing properties to be set in any order must also include that the property doesn't get set at all. While we occasionally (way too often, if you ask me) see examples of the opposite in the BCL, it is a violation of the Principle of Least Surprise if not setting a property leaves a class in an inconsistent state. Extrapolating from that, we should arrive at the conclusion that any writable property that doesn't have its initial value populated in the constructor is optional.

If you don't want a value to be optional, you must force the caller to supply it, and the only way you can do that is by making it a mandatory constructor parameter.

Thus, mandatory constructor parameters signal that a value is required, while writable properties without constructor parameters signal that a value is optional (or, more precisily: that the value has an appropriate default).

If you accept this design principle in general, you must also accept it in the special case of DI.

When I design my APIs, I do so both with an eye towards proper API design in general and how it would fit with a DI Container, but I simply refuse to compromise on the API design to make a particular DI Container happy. Any container that can't deal with ordinary design patterns and well-know coding idioms is broken. If it want to stay in the game, it should re- and conform :)

Coordinator
Feb 22, 2010 at 8:32 PM
ploeh wrote:

I think this proposal introduces a formal way to declare a Leaky Abstraction - something that I'm not particularly fond of.

I don't buy the example concerning User Controls, as I consider it a design smell to have User Controls with dependencies. Controls should be dumb Views with only a little (if any) self-contained behavior.

We can model optional dependencies just fine without any special type signalling this. I prefer using Property Injection for optional dependencies, but overloaded constructors are another prosibility. An optional dependendency always implies some kind of default implementation, either in the form of a proper default implementation, or alternatively in the form of a Null Object. When a dependency is truly optional, it is the dependent's responsibility to instantiate the default dependency until/unless a different implementation is supplied from the outside.

If we want to ensure that the dependent guarantees its invariants, we can use a combination of a readonly backing field combined with Constructor Injection. In that case we would have more than an overloaded constructor because one constructor would call into another by creating a new instance of the default implementation.

If this is not necessary, we can expose the optional dependency as a writable property, but the dependent must still provide the default implementation to itself in the case where it isn't being externally assigned.

In both cases, this gives a DI Container a proper way to opt out of the optional dependency because it doesn't have to serve an instance of the optional dependency. However, if it has a configuration for the optional dependency, it can still serve it.Havi

And I don't by your counter argument ;-)

Having a dependency on a user control is not about the view being dumb. It is about having the View get injected with it's ViewModel which it immediately binds to. It is completely orthagonal to UI logic code that exists in the view. And you neglected the first part which is where the container does not own the creation of the instance. The XAML parser creates the user control, not you. If you wish to leverage the parser, then you will find yourself in this boat. If you do everything manually and virally push locator calls to the container throughout your app you won't.

The general problem exists well beyond the XAML UI case, it exists any time in the framework where there are components which the runtime is responsible for creating, and which have dependencies.

Once you are in those scenarios then the question of whether or not your property dependencies are optional or not comes into play. At that point in time the only way to express dependencies (I believe) is either through properties or through method injection. Assuming that just because it is a property makes it optional is an invalid assumption. Using the constructor is also not an option. What do you do then?

You mentioned the Null Object pattern. That pushes the burden on the host to provide the null object. That won't work for open systems / extensibility scenarios where 3rd party components are simply deployed to the bin-folder. It is the component's responsiblity to determine whether or not it can survive with or witout one of it's services.

Having Optional<T> removes ambiguity allowing a component to very explicitly express the relationship it has with it's dependences is optional. Having Optional<T> in CCA means there is a standard vocabularity for indication this optional nature.

Regards

Glenn

Coordinator
Feb 22, 2010 at 8:58 PM
Edited Feb 22, 2010 at 8:59 PM
kkozmic wrote:

I must say I agree with Mark on this.

If a dependency is optional I think the Right Thing™ to do is to be explicit about it not by throwing some infrastructure level indirection layer, but do it via ctor overloads or settable property.

By leaking abstraction I think Mark means difference between:

 

public class Foo

{

public IBar Bar {get ;set; }

}

 

and

 

public class Foo

{

public IOptional<IBar> Bar {get ;set; }

}
 

 

It is Foo's internal affair whether or not it requires a valid instance of IBar to work or not - it shouldn't announce that to the entire world like this.

Also even if you want to inject mandatory dependency as property after component is created - I think all containers have this ability so it's not really an issue.

The case I was concerned with was the property dependency that is non-optional but it happens to be a property dependency ONLY because it is building up an existing instance. Whether or not it's an internal affair or not is arguable. MEF for example statically analzyes and rejects parts (they are ignored) which are missing dependencies, so it uses that information proactively. If an import is optional however and an implementation is not available, it will set it to Default(T) and move on. Today in MEF this is explicitly declared through a property of the Import attribute.

I can see the arguments though of not leaking the optional-ness of dependencies to the world. Couldn't one use the same argument for Owned<T>, Lazy<T>, etc?

Glenn

 

 

 

 

Coordinator
Feb 22, 2010 at 9:00 PM

Obviously this has touched on a bigger nerve than I expected. It's just a proposal ;-)

Feb 22, 2010 at 9:14 PM

The same type of arguments can (and will) be made against Lazy<T> etc. but we are not yet discussing those :)

I don't understand the argument about XAML instantiation. I've written a fair amount of WPF code using MVVM and have always been able to keep my Views free of any dependencies. The DataTemplate engine of WPF maps ViewModels (which are controlled by my code, and can have all the dependencies injected into them that I want) to Controls, and I never have to assign dependencies to the Controls.

I've written even more ASP.NET MVC code this last year following the same principle.

In neither case have I had the need to to assign dependencies to Controls. A Control just renders data presented by another object. I still consider it a code smell if a renderer needs a dependency. The ViewModel can provide any and all data the renderer may need, including strategies and behavior that controls rendering.

So yes: having a dependency on a user control indicates that the user control is still trying to be too smart. It needs to offload that behavior to the ViewModel.

Coordinator
Feb 22, 2010 at 9:38 PM
Edited Feb 22, 2010 at 9:39 PM

MVC is not a great example as it is designed in a composable fashion. You can plug in IoC at the controller factory or the view engien for starters. Try doing the same thing with web forms. In that case web forms owns the creation of pages and user controls. There are hooks for some things, but not all. You quickly end up in a place where you need to express dependencies with other than a constructor.  You'd be in a similar boat if you tried to add dependencies to a provider in the ASP.NET provider model. ASP.NET creates the provider instance making ctor dependencies not an option.

In the case of WPF/SL the XAML parser creates UI components. If you are applying separated presentation patterns like ViewModel, then your UI needs to have the view model wired to it's DataContext, and one ay to do it is to have the View depend on it's ViewModel. Now you could argue for this case that an attached property which sets the DataContext is cleaner, but having the View depend on it's ViewModel is valid, in the same way that having a UserControl depend on it's Presenter in web forms.

 

 

 

 

Coordinator
Feb 22, 2010 at 9:44 PM

DataTemplates are one approach that does address the problem though not without limitations, for example when the model needs to trigger things in the view that cannot be expressed through simple databinding.

Coordinator
Feb 22, 2010 at 10:37 PM

Once again blown away by the kzu :)

public Foo(IBar bar /* required */, IBaz baz = null) { }

Interesting idea! I'll have to give this one some thought, but it seems like it ticks all of the boxes.

Nick

Coordinator
Feb 23, 2010 at 5:19 AM

Very nice idea @Kzu for optional constructor params. I would still like to find a way to express optionals on properties rather than assuming they are optional by convention because they are properties.

Developer
Feb 23, 2010 at 5:48 AM
how about:

[DefaultValue(null)]
public IFoo OptionalFoo { get; set; }

;)

/kzu

--
Daniel Cazzulino | Developer Lead | XML MVP | Clarius Consulting | +1 425.329.3471


On Tue, Feb 23, 2010 at 03:19, gblock <notifications@codeplex.com> wrote:

From: gblock

Very nice idea @Kzu for optional constructor params. I would still like to find a way to express optionals on properties rather than assuming they are optional by convention because they are properties.

Read the full discussion online.

To add a post to this discussion, reply to this email (cca@discussions.codeplex.com)

To start a new discussion for this project, email cca@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Feb 23, 2010 at 7:13 AM

It could be my Windsor roots showing, but I would favor construtor-required properties-optional in the spirit of convention over configuration.

Another concern could be - would Optional<T> assume you were referencing the service through a 'public T value' property? Would you compose decorations like Optional<Lazy<T>>? If so _olt.value.value.Hello(); could be suboptimal...

Swiching gears for a second... If a property is optional by default, should [Required] data annotation attribute on the property be understood by IoC as a signal to throw an exception if the service or value can't be assigned? (Kind of the opposite of the =null indication in the ctor)

That would be nice if there's an abstract base for a class of services which has some required dependencies, but you don't want to burden the descendants of the service base with a bunch of pass-it-along constructor arguments.

 

Feb 23, 2010 at 7:39 AM

Glenn: Now you are discussing the validity of Property Injection over Constructor Injection. This is a discussion on which I have yet to say my final word, but we were actually discussing the validity of Optional<T>.

I still find Optional<T> redundant because the compiler never forces you to assign a value to a property. Hence, all writable properties should be interpreted as optional.

That MEF has a different interpretation is really not an argument, because the only (semi-)compelling argument for Optional<T> I've seen so far is that it explicitly communicates intent. That's fine, but there's no reason to explicitly communicate intent for something which is already given. We don't put [Mandatory] attributes on all constructor parameters either, becuase everyone knows that you must supply values for constructor arguments.

We need to explicitly communicate intent when we deviate from known patterns and idioms. MEF is the deviator here, so it should be up to it (and other violators) to explicitly communicate that they are different - not the other way around.

Coordinator
Feb 23, 2010 at 4:02 PM

It has nothing to do with MEF being the "deviator" as you so nicely put it ;-) It also has nothing to do with user controls specifically. Which btw I respectfully disagree with your assesment and I too have been using UI patterns for a long time. Having some wiring code in a view is not a violation in anyway of any UI patterns I know. Having business logic is another story.

The issue here has to do with injection of instances for which the container doesn't control the lifetime. When you are dealing with legacy frameworks you will encounter these situations quite often. There are work arounds but they introduce a bunch of complexity. If you want to build up an existing instance, then at that point in time constructor dependencies are no longer an option. Which raises the question of should they be assumed to automatically be optional as at in those situations a property dependency is NO different than a constructor dependency in the sense that I am not using on a property because it's optional, I am using it becuase I have no other option.

Glenn

Feb 23, 2010 at 6:03 PM

I understand what you are saying, but the discussion boils down to whether one thinks that Optional<T> is a desirable or a redundant degree of explicitness. At this point I think we will have to agree to disagree :)

Coordinator
Feb 23, 2010 at 7:04 PM

I am completely agreeable at disagreeing :-)

Glenn

Developer
Feb 25, 2010 at 5:25 PM
So, for properties, couldn't we use [DefaultValue(null)]? That would cover all cases for c#4 (with defaulted ctor arguments for optionality opt-in).

I'd rather do without custom types if possible (nice to have, anyways)

/from crappy S60 mobile OS

-original message-
Subject: Re: Proposal: Optional<T> [cca:151961]
From: "gblock" <[email removed]>
Date: 23/02/2010 5:04 pm

From: gblock

I am completely agreeable at disagreeing :-)Glenn