Option - Perustaja/Polyglot GitHub Wiki
Inspired by Rust's Option
monad which is very similar to Maybe
in F# and Haskell.
The Option
monad represents a discriminated union type of either Some
or None
. For all implementations,
they should act as close to possible as they do in Rust.
var something = Option<string>.Some("Hello");
var nothing = Option<string>.None;
Two functions are provided to see check if it is Some
or None
.
var o = Option<string>.Some("Noniin");
Console.WriteLine(o.IsSome()); // Prints true
Console.WriteLine(o.IsNone()); // Prints false
Unwrap()
returns the underlying value if the value is Some
, or throws an exception if it is None
.
int some = Option<int>.Some(10).Unwrap(); // Returns 10
int none = Option<int>.None.Unwrap(); // Throws an exception!
Provides a default value, if the default value is the result of a function call, it must be eagerly evaluted (the function must be invoked within the call toUnwrapOr()
).
int some = Option<int>.Some(5).UnwrapOr(10); // Returns 5, the underlying value
int none = Option<int>.None.UnwrapOr(10); // Returns 10, the default value
Provides a fallback function to lazily evaluate in a closure if the current is None
.
int num = 10;
int some = Option<int>.Some(5).UnwrapOrElse(() => num * 5); // Returns 5, the underlying value
int none = Option<int>.None.UnwrapOrElse(() => num * 5); // Returns 50, the result of the default function
Map()
is a function for mapping an Option<T>
to an Option<U>
by invoking a passed function. If the current is Some
, the function is invoked
with the underlying value, returning a new Some
of a different type. If it is None
, the result is still None
.
var someOpt = Option<int>.Some(10).Map(o => o.ToString()); // Returns Some with underlying value "10"
var none = Option<int>.None.Map(o => o.ToString()); // Returns None
Provides a fallback value to return in case the current is None
.
string some = Option<int>.Some(10).MapOr("Default", o => o.ToString()); // Returns "10"
string none = Option<int>.None.MapOr("Default", o => o.ToString()); // Returns "Default"
Provides a fallback function to lazily evaluate in a closure if the current is None
.
string greeting = "hello";
string r = Option<int>.None.MapOrElse(
() => greeting.ToUpper,
s => s.ToString()
);
// r : "HELLO"
AndThen()
allows chaining. Each function in the chain returns an Option
, calling the passed function on its underlying value if Some
, or returning None
if there isn't one. If any of the calls in the chain return None
, the end result is None
.
// Assume the following function exists
private Option<int> SquareIfEven(int n)
=> n % 2 == 0
? Option<int>.Some(n * n)
: Option<int>.None;
public void SomeOtherScope()
{
var o = Option<int>.Some(2);
var r = o.AndThen(SquareIfEven).AndThen(SquareIfEven).Unwrap(); // Returns 16
o = Option<int>.Some(3);
var r = o.AndThen(SquareIfEven).AndThen(SquareIfEven).Unwrap() // Throws an exception
}
Returns None
if current option has no value or the option has a value but the predicate returns false when evaluated with the underlying Some
value. Returns Some
if the current is Some
and the predicate evaluates to true.
var none = Option<int>.Some(3).Filter(n => n % 2 == 0); // is None
var some = Option<int>.Some(2).Filter(n => n % 2 == 0); // is Some with underlying value 2
Returns the current Option
if Some
, else returns the argument. Note that this does not guarantee the other is Some
.
var original = Option<int>.None.Or(Option<int>.Some(10));
// original : Some(10)
Transforms the Option
into a Result
. If current is Some
, returns Ok
with the same underlying value, else returns Err
with underlying value set to the argument.
var okay = Option<int>.Some(10).OkOr<string>("Error!");
// okay : Ok(10)
var notOkay= Option<int>.None.OkOr<string>("Error!");
// okay : Err("Error!")
Performs the same functionality as OkOr
but lazily evaluates the error function to use it as the error value.
var okay = Option<int>.Some(10).OkOrElse<string>(() => 10.ToString());
// okay : Ok(10)
var notOkay = Option<int>.None.OkOrElse<string>(() => 10.ToString());
// notOkay : Err("10")
This can be used for I/O operations or when a side effect is desired. This is made to mimic
the match
block in Rust, but obviously has its downsides as you cannot handle many different values
like in Rust.
var o = Option<string>.Some("10");
o.Match(
s => Console.WriteLine(s),
() => someFile.WriteLine("Empty!")
);
// Prints "10" to the console
var o = Option<string>.None;
o.Match(
s => Console.WriteLine(s),
() => someFile.WriteLine("Empty!")
);
// Prints "Empty!" to some file
Options
are safe to use in hashed collections. It is also safe to use the ==
operator on them.