Optuple is a .NET Standard library that enables a tuple of Boolean and some
type (T), i.e. (bool, T), to have the same semantics as an option type
found in most functional languages.
An option type is a discriminated union that either represents the absence of a value (none) or the value that's present (some). For example, F# has such a type that is defined as:
type Option<T> = | None | Some of TOptuple, however, does not define any such type. Instead it primarily supplies
extension methods for any (bool, T) (like Match, Map and more) to be
treated like an option type. Suppose a value x of type T, then
(true, x) will be treated like Some x and (false, _) will
be treated like None. Note that in the none case, when the first
element is false, Optuple completely ignores and disregards the second
element.
A library that wants to expose optional values needs no dependency on Optuple.
It can just expose (bool, T) for some type T. The client of the library
can use Optuple independently to get “optional semantics”.
To use Optuple simply import the following namespace:
using Optuple;An auxiliary namespace is also provided:
using Optuple.Linq; // LINQ query syntax supportThe most basic way to create optional values is to use the static Option
class:
var none = Option.None<int>();
var some = Option.Some(42);Similarly, a more general extension method is provided, allowing a specified predicate:
string str = "foo";
var none = Option.SomeWhen(str, s => s == "bar"); // Return None if predicate is violated
var none = Option.NoneWhen(str, s => s == "foo"); // Return None if predicate is satisfiedClearly, optional values are conceptually quite similar to nullables. Hence, a method is provided to convert a nullable into an optional value:
int? nullableWithoutValue = null;
int? nullableWithValue = 2;
var none = nullableWithoutValue.ToOption();
var some = nullableWithValue.ToOption();When retrieving values, Optuple forces you to consider both cases (that is if a value is present or not).
Firstly, it is possible to check if a value is actually present:
var isSome = option.IsSome(); //returns true if a value is present
var isNone = option.IsNone(); //returns true if a value is not presentIf you want to check if an option option satisfies some predicate, you can
use theExists method.
var isGreaterThanHundred = option.Exists(val => val > 100);The most basic way to retrieve a value from an Option<T> is the following:
// Returns the value if present, or otherwise an alternative value (42)
var value = option.Or(42);Similarly, the OrDefault function simply retrieves the default value for
a given type:
var none = Option.None<int>();
var value = none.OrDefault(); // Value 0var none = Option.None<string>();
var value = none.OrDefault(); // nullIn more elaborate scenarios, the Match method evaluates a specified
function:
// Evaluates one of the provided functions and returns the result
var value = option.Match(x => x + 1, () => 42);
// Or written in a more functional style (pattern matching)
var value = option.Match(
some: x => x + 1,
none: () => 42
);There is a similar Match function to simply induce side-effects:
// Evaluates one of the provided actions
option.Match(x => Console.WriteLine(x), () => Console.WriteLine(42));
// Or pattern matching style as before
option.Match(
some: x => Console.WriteLine(x),
none: () => Console.WriteLine(42)
);A few extension methods are provided to safely manipulate optional values.
The Map function transforms the inner value of an option. If no value is
present none is simply propagated:
var none = Option.None<int>();
var stillNone = none.Map(x => x + 10);
var some = Option.Some(42);
var somePlus10 = some.Map(x => x + 10);Finally, it is possible to perform filtering. The Filter function returns
none, if the specified predicate is not satisfied. If the option is already
none, it is simply returned as is:
var none = Option.None<int>();
var stillNone = none.Filter(x => x > 10);
var some = Option.Some(10);
var stillSome = some.Filter(x => x == 10);
var none = some.Filter(x => x != 10);If you statically import OptionModule:
using static Optuple.OptionModule;it will make the following common methods available for use without type qualification:
SomeNone<>SomeWhenNoneWhen
This permits you to, for example, simply write Some(42) and None<int>()
instead of Option.Some(42) and None<int>(), respectively.
Although options deliberately don't act as enumerables, you can easily convert
an option to an enumerable by calling the ToEnumerable() method:
var enumerable = option.ToEnumerable();By importing the Optuple.Collections namespace, you also get the following
extension methods for sequences (IEnumerable<>) that are like their LINQ
counterparts but return an option:
FirstOrNone: likeFirstOrDefaultbut returns an optionLastOrNone: likeLastOrDefaultbut returns an optionSingleOrNone: likeSingleOrDefaultbut returns an option
Then there is:
Filter, which given a sequence of options, will return a sequence of x values from those options in the original sequence that are some x.ListAll, which given a sequence of options, will return some list of x if all options in the original sequence are some x; otherwise it returns none list.
Optuple supports LINQ query syntax, to make the above transformations somewhat cleaner.
To use LINQ query syntax you must import the following namespace:
using Optuple.Linq;This allows you to do fancy stuff such as:
var personWithGreenHair =
from person in FindPersonById(10)
from hairstyle in GetHairstyle(person)
from color in ParseStringToColor("green")
where hairstyle.Color == color
select person;In general, this closely resembles a sequence of calls to FlatMap and
Filter. However, using query syntax can be a lot easier to read in complex
cases.
Two optional values are equal if the following is satisfied:
- The two options have the same type
- Both are none, both contain null values, or the contained values are equal
Credit: A large portion of this documentation was dervied from that of the project Optional. Thank you, Nils Lück!