UniOption
Provides an implementation of the Option type for Unity. This is similar to the Option type from Rust, or the Maybe type from Haskell.
Contains types Option<T> for reference types, and ValueOption<T> for value types.
Usage
Option<T> provides the following methods:
- Some
- None
- Map
- MapValue
- MapObject
- Reduce
- Do
- DoAsync (UniTask)
- Where
- WhereNot
- OfType
- Or
- Match
- ToEnumerable
- Zip
ValueOption<T> is the same as Option<T>, but for value types. It provides the same methods, but instead of MapValue it is MapObject.
Any type can be implicitly converted to an Option<T> or ValueOption<T>. null is converted to None, and any other value is converted to Some(value).
Alternatively, Any reference or value type can be converted to an Option<T> or ValueOption<T> using the ToOption() and ToValueOption() extension methods.
Examples
Basics
// Create a new Option
Option<string> some = "Hello World";
Option<string> none = null;
ValueOption<int> someValue = 42;
//Perform operations on the options
some.Map(s => s.ToUpper())
.Reduce("Default");
.Do(s => Debug.Log(s))// Prints "HELLO WORLD"
none.Map(s => s.ToUpper())
.Reduce("Default");
.Do(s => Debug.Log(s))// Prints "Default"
someValue.Map(i => i * 2)
.Reduce(0);
.Do(i => Debug.Log(i))// Prints "84"
//Async operations
some.Map(s => s.ToUpper())
.DoAsync(async s =>
{
await UniTask.Delay(1000);
Debug.Log(s);
}).Forget();
Where/WhereNot
var stringOption = "TestString".ToOption()
.Where(s => s.Length > 5)
.WhereNot(s => s.Length > 15)
.Do(s => Debug.Log(s))// Prints "TestString"
var intOption = 5.ToValueOption()
.Where(i => i >= 5)
.Do(i => Debug.Log(i))// Prints "5"
OfType
var stringOption = "TestString".ToOption()
.OfType<string>()
.Do(s => Debug.Log(s))// Prints "TestString"
Or
string nullString = null;
var stringOption = nullString.ToOption()
.Or("Default")
.Do(s => Debug.Log(s))// Prints "Default"
Match
var option = "Hello World".ToOption();
var result = option.Match(some: s => s.ToUpper(),
none: () => "Default");
ToEnumerable
var stringOption = "TestString".ToOption();
foreach (var s in stringOption.ToEnumerable()) {
Debug.Log(s);// Prints "TestString"
}
Zip
Returns a ValueOption<(T1,T2)> containing the result of the input function if both options are Some, or None if either option is None.
var stringOption = "TestString".ToOption()
.Zip(5);//Contains ValueTuple ("TestString", 5)
Avoiding Closures
In this example:
public string DoSomething(string value, Option<string> option) {
return option.Match(s => value + s, () => value);
}
A closure is used to capture value. This can be detrimental to performance, and should be avoided.
Instead, the Match, Do and DoAsync methods have overloads that avoid closures by passing the context as a parameter:
public string DoSomething(string value, Option<string> option) {
return option.Match(some:(s, context) => context + s,
context:value,
none:context => context);
}
In the case of Do, if the context needs to be mutated, it may be passed with a ref parameter:
public string DoSomething(int value, Option<string> option) {
return option.Do(some:(string s, ref int context) => {
s += context.ToString();
context++;
return s;
},
context:ref value,
none:ref int context => context--);
}
API References
API documentation can be found here.