Skip to content
David Arno edited this page Feb 17, 2020 · 4 revisions

Option<T>

SuccincT.Options.Option<T>

Provides an optional value of type T. Modelled on F# options. Either contains a T value, or none.


Construction

Instances of Option<T> can be created via various means:

  1. They can be created directly: var o = new Option<string>(s). If s is null, then a none is created, otherwise the option has some(s) as its value.
  2. Use the two static methods Option<T>.None() and Option<T>.Some(T value) to create a none or some value, respectively. In this case, if Option<T>.Some(null) is called, an ArgumentNullException is thrown.
  3. A value of T can be directly used to create an option, eg Option<int> o = 1 creates o as an Option<int> containing the value, 1 and Option<string> o = null will create a none.

Equality

Option<T> provides equality via Equals and the == & != operator pair. To be equal, both options must have some(value) and the values must be equal, or both must be none. Additionally, null is always equal to none:

var a = Option<int>.Some(1);
var b = Option<int>.Some(1);
var c = Option<int>.Some(2);
var d = Option<int>.None();
var e = Option<int>.None();

// The following expressions are all true
a == a
a == b
a != c
d == e
a.Equals(b)
d.Equals(e)

Functional Use

For details on using Succinc<T>'s own pattern pattern feature with the Either type, see Pattern matching on Option<T>.

As of v8, C# itself has excellent pattern matching support built in to the language. Option<T> therefore provides the following deconstruct to allow it's use with position pattern matching:

public void Deconstruct(out Option state, out T value);

Option is an enum with two values, None and Some. When state == None, value will contain default(T). When state == Some, value will contain the value stored in the option.

This then allows an option to be pattern matched as follows:

var option = new Option<int>(2);

var result = option is (Some, var i) ? $"Option has int value, {i}" : "Empty";

Note, in order to support pattern matching, the previously supplied deconstruct in Option<T> had to be removed:

public void Deconstruct(out bool hasValue, out T value)

This introduces a breaking change to any existing deconstruct code.

For a quick fix, the general form var (hasValue, value) = option can be expressed as:

var (hasValue, value) = option is (Some, var v) ? (true, v) : (false, default);

but it's not pretty. So you'll very likely want to refactor the code to accommodate this change at some stage.

Imperative Use

As an alternative to using an option in a functional style, its value can be directly accessed by more traditional, imperative style C# code. Three read-only properties are provided for this purpose:

HasValue

public bool HasValue { get; }

True if the option has a value; else false.

Value

public T Value { get; }

If HasValue is true, this will return the value held by the option. Otherwise an InvalidOperationException will be thrown.

ValueOrDefault

public T ValueOrDefault { get; }

If HasValue is true, this will return the value held by the option. Otherwise it will return default(T)

Extensions

Option<T> provides a number of extension methods for manipulating the value, if present.

Choose<T>

public static IEnumerable<T> Choose<T>(this IEnumerable<Option<T>> options)

Returns a new enumeration of options containing just those with a value. Equivalent to executing:

from option in options where option.HasValue select option;

Flattern<T>

public static Option<T> Flatten<T>(this Option<Option<T>> option)

"Flatterns" (or de-nests) an option of option of T.

For an Option<Option<T>> with none, an Option<T> with none is returned.

For an Option<Option<T>> with a value, an Option<T> with that same value is returned.

Map<TInput, TOutput>()

public static Option<TOutput> Map<TInput, TOutput>(this Option<TInput> input,
                                                   Func<TInput, TOutput> f)

Maps the input option to an output option, via the supplied function.

For an Option<TInput> with none, an Option<TOutput> with none is returned.

For an Option<TInput> with a value, an Option<TOutput> with the result of applying f(input.Value) to that value, is returned.

Or<T>()

public static Option<T> Or<T>(this Option<T> option, Option<T> anotherOption)

Returns option if it has a value, otherwise anotherOption is returned.

public static Option<T> Or<T>(this Option<T> option, 
                              Func<Option<T>> lazyAnotherOption)

Returns option if it has a value, otherwise lazyAnotherOption() is called, and the result of that function is returned.

Some<T>()

public static Option<T> Some<T>(this T value)

Returns an Option<T> with Value set to the value of the item converted. This allows the easy creation of an option from a value, using var:

var option = "a string".Some();
// option is Option<string> and option.Value == "a string"

Finally, a number of extension methods for IEnumerable<T>, IDictionary<T> and for handling casting and null in general types are provided. See Extension methods for existing types, which use Option<T> for details.

Clone this wiki locally