-
Notifications
You must be signed in to change notification settings - Fork 15
PatternMatchingOtherTypesByType
[Note: this page needs work as the examples are poor]
This pattern matching guide is split into the following sections:
- Pattern matching on discriminated unions.
- Pattern matching on options
- Pattern matching on Success<T>
- Pattern matching on tuples
- Pattern matching on
ValueOrError
- Type-based pattern matching for all other types - Covered here.
- Value-based pattern matching on all other types
Type-based pattern matching on general types is achieved via a single extension method, TypeMatch()
, which must be followed by .To<ResultType>()
. The reason for using To<T>()
rather than just providing a TypeMatch
that specifies the return type, is due to the way C# handles generics and methods. The rule is simple: either all type parameters can be inferred and so none need be specified, or all type parameters must be specified. As such, the following style would have to be used when, in this case, matching on an int
and returning a string
:
var result = 100.TypeMatch<int, string>()...
By using TypeMatch().To<ResultType>()
, this becomes:
var result = 100.TypeMatch().To<string>()...
It's a subtle change, but in my opinion it aids readability as the type parameter is focused solely on the return type, rather than restating the value/reference type to be matched.
Note that an action-based version is not provided. This is because the new pattern matching features in C# 7 provide this ability already, so there seems little point in replicating it.
The generalised syntax for patterns can be expressed using BNF-like syntax:
result = {item}.TypeMatch().To<{result type}>()
[CaseOfExpression]...
[ElseExpression]
.Result();
CaseOfExpression ==>
.Case<{some type}>()[WhereExpression].Do({value of some type} => {result type expression}) |
.Case<{some type}>()[WhereExpression].Do({result type expression})
WhereExpression ==>
.Where({item} => {boolean expression})
ElseExpression ==>
.Else({item} => {result type expression}) |
.Else({result type expression})
To explain the above syntax:
-
{}
denotes a non-literal, eg{void expression}
could be the empty expression,{}
, or something likeConsole.WriteLine("hello")
. - Items in
[]
are optional. -
|
isor
, ie[x|y]
reads as "an optional x or y". -
...
after[x]
means 0 or more occurrences of x. -
==>
is a sub-rule, which defines the expression on the left of this symbol.
For the purposes of these examples, assume we have an interface, IAnimal
, and calsses that implement it: Cat
, Dog
and Lizard
.
public static bool IsAMammal(IAnmial animal) =>
animal.TypeMatch().To<bool>()
.CaseOf<Cat>().Do(x => true)
.CaseOf<Dog>().Do(x => true)
.CaseOf<Lizard>().Do(x => false)
.Result();
Clearly, specifying all three animals is long-winded. We can simplify it by using Else
:
public static bool IsAMammal(IAnmial animal) =>
animal.TypeMatch().To<bool>()
.CaseOf<Lizard>().Do(x => false)
.Else(x => true)
.Result();
One further change can be made. We are supplying a parameter, x
to all the lambdas, which isn't then used. In this case, we can dispense with the lambdas and just specify the return values:
public static bool IsAMammal(IAnmial animal) =>
animal.TypeMatch().To<bool>()
.CaseOf<Lizard>().Do(false)
.Else(true)
.Result();
Obviously, the above could be simplified further just to:
public static bool IsAMammal(IAnmial animal) => animal is Lizard;
but the idea is to show a simple use of the feature, so hopefully you'll forgive me a possibly bad example of this pattern matching usage.
Let's redefine our types. IAnimal
now has a boolean property, CanFly
. So a new set of animal classes are created: Mammal
, Bird
, Reptile
.
Using Where
, we can then, for example, test if the animal is a bird, capable of flight:
public static bool IsAFlyingBird(IAnmial animal) =>
animal.TypeMatch().To<bool>()
.CaseOf<Bird>().Where(bird => bird.CanFly).Do(true)
.Else(false)
.Result();
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