-
Notifications
You must be signed in to change notification settings - Fork 15
UnionT1T2T3
SuccincT.Unions.Union<T1, T2, T3>
Provides a union of types T1
, T2
and T3
. Modelled on F# discriminated unions. Contains one of a T1
value, T2
value or a T3
value exclusively.
Instances of Union<T1, T2, T3>
are either created via the overloaded constructor or via a union creator. Because unions can be directly created, T1
, T2
and T3
must be of sufficiently different types for the compiler to be able to choose the correct constructor.
public Union(T1 value)
public Union(T2 value)
public Union(T3 value)
Using constructors,union instances are created just like any other class:
var v1 = new Union<int, string, double>(1);
var v2 = new Union<int, string, double>("2");
var v3 = new Union<int, string, double>(1.0);
public static UnionCreator<T1, T2, T3> Creator()
Creates an instance of a union creator, which can be used thus:
var creator = Union<int, string, double>.Creator();
var v1 = creator.Create(1);
var v2 = creator.Create("2");
var v2 = creator.Create(3.0);
Whilst Union<T1, T2, T3>
is a class, not a struct, it overrides both Equals
and the ==
& !=
operator pair to provide value-based equality, as demonstrated by the following code:
var creator = Union<int, string, double>.Creator();
var a = creator.Create(1);
var b = creator.Create(1);
var c = creator.Create(2);
var d = creator.Create("1");
var e = creator.Create("1");
var f = creator.Create(1.0);
// The following expressions are all true
a == a
a == b
a != c
b != d
d == e
a.Equals(b)
d.Equals(e)
f != a // int and double are different types, so the values aren't compared.
Union<T1, T2, T3>
uses Succinc<T>'s pattern matching capabilities to perform an action or generate a result according to its value. Two versions of the match method are directly supported by Union<T1, T2, T3>
:
public OptionMatcher<T1, T2, T3> Match()
Match()
supports the construction of patterns resulting in actions (void methods) to invoke upon a sucessful match. The pattern definition must be terminated with Exec()
.
The format of an option match pattern is as follows:
option.Match()
.Case1()|CaseOf<T1>()<optional guard>.Do(value => action on value)
.Case2()|CaseOf<T2>()<optional guard>.Do(value => action on value)
.Case3()|CaseOf<T3>()<optional guard>.Do(value => action on value)
[.Else(union => action on no match) |
.IgnoreElse()]
.Exec();
Case1()
, Case2()
and Case3()
can take an optional guard of two forms:
.CaseN().Of(value1).Or(value2).Or(value3)...Do(value => action on value)
.CaseN().Where(value => boolean expression).Do(value => action on value)
CaseOf<type>()
can likewise take those optional guards:
.CaseOf<type>().Of(value1).Or(value2).Or(value3)...Do(value => action on value)
.CaseOf<type>().Where(value => boolean expression).Do(value => action on value)
Multiple Case1()
, Case2()
, Case3()
and CaseOf<type>()
expressions may be defined. Each is compared in turn against that type's value (assuming there is one) until a match is found. The action is then invoked and no further matching occurs.
The Else
action is used if no match was found. The union itself is passed as a parameter to the associated action. If no action is required when no match occurs, IgnoreElse
can be used instead.
If no match is found, and no Else()
or IgnoreElse
is defined, a SuccincT.PatternMatchers.NoMatchException
will be thrown
If CaseOf<type>
is used, with type
being different to both T1
and T2
, a SuccincT.PatternMatchers.InvalidCaseOfTypeException
will be thrown
public OptionMatcher<T1, T2, T3, TResult> Match<T>()
Match<T>()
supports the construction of patterns resulting in functions returning a TResult
to invoke upon match. The pattern must be terminated with Result()
.
The format of an option match pattern is as follows:
option.Match<T>()
.Case1()|CaseOf<T1>()<optional guard>.Do(value => func resulting in a T)
.Case2()|CaseOf<T2>()<optional guard>.Do(value => func resulting in a T)
.Case3()|CaseOf<T3>()<optional guard>.Do(value => func resulting in a T)
.Else(union => no match func resulting in a T)
.Result()
Case1()
, Case2()
and Case3()
can take an optional guard of two forms:
.CaseN().Of(value1).Or(value2).Or(value3)...Do(value => func resulting in a T)
.CaseN().Where(value => boolean expression).Do(value => func resulting in a T)
CaseOf<type>()
can likewise take those optional guards:
.CaseOf<type>().Of(value1).Or(value2).Or(value3)...Do(value => func resulting in a T)
.CaseOf<type>().Where(value => boolean expression).Do(value => func resulting in a T)
Multiple Case1()
, Case2()
, Case3()
and CaseOf<type>()
expressions may be defined. Each is compared in turn again that type's value (assume there is one) until a match is found. The function is then invoked and no further matching occurs.
The Else
function is used if no match was found. The union itself is passed as a parameter to the associated function.
If no match is found, and no Else()
defined, a SuccincT.PatternMatchers.NoMatchException
will be thrown
If CaseOf<type>
is used, with type
being different to both T1
and T2
, a SuccincT.PatternMatchers.InvalidCaseOfTypeException
will be thrown
For further details see the pattern matching options guide.
As an alternative to using an union 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:
public TResult Value<TResult>()
Returns the value of the union directly. If the value of the union is not of the TResult
type, an InvalidOperationException
will be thrown.
public Variant Case { get; }
Returns Variant.Case1
if the value is of T1
; Variant.Case2
if the value is of T2
; or Variant.Case3
if the value is of T3
.
public T1 Case1 { get; }
If Case
is Variant.Case1
, this will return the value held by the union. Otherwise an InvalidOperationException
will be thrown.
public T2 Case2 { get; }
If Case
is Variant.Case2
, this will return the value held by the union. Otherwise an InvalidOperationException
will be thrown.
public T3 Case3 { get; }
If Case
is Variant.Case3
, this will return the value held by the union. Otherwise an InvalidOperationException
will be thrown.
public bool HasValueOf<T>()
For the current case, Case1
, Case2
or Case3
, if that case's type matches T
then the result is true
; otherwose it's false
. For example,
var union = new Union<int, string, Plants>(2);
var hasInteger = union.HasValueOf<int>(); // hasInteger will be true
Action
/Func
conversionsCycle
methods- Converting between
Action
andFunc
- Extension methods for existing types that use
Option<T>
- Indexed enumerations
IEnumerable<T>
cons- Option-based parsers
- Partial function applications
- Pattern matching
- Pipe Operators
- Typed lambdas
Any
Either<TLeft,TRight>
None
Option<T>
Success<T>
Union<T1,T2>
Union<T1,T2,T3>
Union<T1,T2,T3,T4>
Unit
ValueOrError