This project includes types and higher-order functions adopted from functional programming.
The Option<T> type encapsulates an optional value. It is usefull when the actual value (of type T) might not exist. Option is defined as a union type with two cases: Some and None.
var someInt = Option.Some(42); // creates an instance of Some<int>
var noneInt = Option.None<int>(); // creates an instance of None<int>var someInt = 5.ToOption(); // creates Some<int>
string s = null;
var noneString = s.ToOption(); // yields None<string>Option<string> s = "hello"; // creates an instance of Some<string>int? i = 42;
var o = i.ToOption(); // creates an instance of Some<int>If null is passed as an argument to Option.Some<T>(T value) it will yield None:
var none = Option.Some<string>(null); // the result is None<string>The extraction of the value should be easy but safe. It is supposed to be made difficult to extract the value when it is null (in case of None) to prevent any kind of NullReferenceExceptions. Also the consumer should be forced to handle both cases of an exisitng value (Some) and non-existing value (None). (This concept is close to pattern matching from FP, e.g. F#.)
The value can be retrieved by calling the Match method. This method has the following signature:
TResult Match<TResult>(Func<T, TResult> onSome, Func<TResult> onNone) or in F# notation: ((T -> TResult)*(unit -> TResult)) -> TResult
Option.Some(49.9m)
.Match(
x => String.Format("Result = {0} %", x.ToString("F")),
() => "An error occurred.");The DefaultIfNone(T defaultValue) extension method will return the inner value of an Option instance. If the Option is None a default value that is specified as an argument will be returned.
var x = Option.None<int>().DefaultIfNone(-1); // x will be -1
var y = Option.Some(42).DefaultIfNone(-1); // y will be 42Choice<T1, T2> is a type which represents either a value of type T1 or a value of type T2.
Create an instance of Choice that represents Choice 1 of 2:
var c = Choice.NewChoice1Of2<decimal, string>(2.5m);Create an instance of Choice that represents Choice 2 of 2:
var c = Choice.NewChoice2Of2<decimal, string>("An error occurred.");An instance of Choice can be created from an instance of Option. If the Option is Some then the Choice will represent the value of the Option instance.
var c = Option.Some(42).ToChoice("No value specified."); // c represents 42If the Option is None it will represent an alternative value which was passed as an argument.
var c = Option.None<int>().ToChoice("No value specified."); // c represents "No value specified"The extraction of the inner value of the choice type can be done with Match() similar to the extraction of Option values.
var result = Choice.NewChoice1Of2<string, int>("world")
.Match(
onChoice1Of2: x => String.Format("Hello {0}", x),
onChoice2Of2: x => String.Format("The number is {0}", x));Fun.Create() helps with creating instances of Func.
This
var f = new Func<decimal, decimal, decimal, Option<decimal>((x1, x2, x3) => (x1 + x2) / x3));can be written as
var f = Fun.Create((decimal x1, decimal x2, decimal x3) => (x1 + x2) / x3));The extension method ReturnOption() takes the result from a fuction and returns it wrapped in the option type. If the result is null it returns None.
var f = Fun.Create((int i) => i%2 == 0 ? "foo" : null)
.ReturnOption(); // returns Option.None<string> if an odd number is passed as an argumentThe extension method OnException() internally wraps a try catch around the execution and returns None if an exception is thrown. It only extends functions with return type Option.
var result = Fun.Create((decimal x, decimal y) => x/y)
.ReturnOption()
.OnExceptionNone(); // if 0 is passed as the second argument result will be None<decimal>There are no parsing function included. But it is very easy to create them if needed with any operation that might return null or throw an exception.
var parseInt = Fun.Create((string s) => Int32.Parse(s)).ReturnOption().OnExceptionNone();
var i = parseInt("sdfs"); // i will be Option.None<int>Curry() returns the curried version of a function. (This is i.e necessary for the option type to act like an applicative functor.)
var result = Fun.Create((decimal x, decimal y) => x/y).Curry(); // yields Func<decimal, Func<decimal, decimal>>var result = Fun.Create((decimal x, decimal y) => x/y)
.ReturnOption()
.OnExceptionNone()
.Curry() // returns Func<decimal, Func<decimal, Option<decimal>>
.ToOption() // lifts the function into the option type
.Apply(ReadDecimal()) // applies the result from ReadDecimal() and returns Func<decimal, Option<decimal>>
.Apply(ReadDecimal()) // applies the result from ReadDecimal() and returns Option<decimal>
.Select(x => x * 100) // maps a function over the inner value if option is Some
.Match(
x => String.Format("Result = {0} %", x.ToString("F")),
() => "An error occurred.");
Console.WriteLine(result);Also the Linq query syntax is supported.
var output =
(
from v1 in ReadDecimal()
from v2 in ReadDecimal()
from result in Divide(v1, v2)
select result * 100
)
.Match(
x => String.Format("Result = {0} %", x.ToString("F")),
() => "An error occurred.");
Console.WriteLine(output);(
from v1 in ReadDecimal().ToChoice(Failure.Create(Error.CannotNotParse1StInput))
join v2 in ReadDecimal().ToChoice(Failure.Create(Error.CannotNotParse2NdInput)) on 1 equals 1
from result in Divide(v1, v2).ToChoice(Failure.Create(Error.CannotDivideByZero))
select result
)
.Match(
x => Console.WriteLine("Result = {0}", x),
errors => errors.ToList().ForEach(x => Console.WriteLine(x.GetDisplayName())));