Last time, I just stopped before generating any real expression tree… Now is the perfect time to continue the process !
In order to handle match expressions where none of the expressions would match a given candidate, I decided to use an option type. This allows the generation of this type of expressions:
Func<double, Option<double>> matchSomeOrNone =
(double d) =>
(d > 0.0)
? Option<double>.Some(d)
: Option<double>.None();
Given this these options, the translation of last time’s sample code:
var stringPattern = PatternMatcher
.CreateSwitch<string, double>()
.Case("TEST" , s => Math.PI)
.Case("TEST_1" , s => Math.E)
.Case((string)null , s => 0.0)
.Case(s => s.StartsWith("TEST__") , s => Math.E * 2)
.CaseElse( s => s.Length);
would become:
Func<string, Option<double>> generatedFuncOption =
(string s) =>
(s == "TEST")
? Option<double>.Some(Math.PI)
: (s == "TEST_1")
? Option<double>.Some(Math.E)
: (s == null)
? Option<double>.Some(0.0)
: s.StartsWith("TEST__")
? Option<double>.Some(Math.E * 2)
: Option<double>.Some(s.Length);
In order to instantiate option types in LINQ expressions, I have to use the corresponding MethodInfo instances, obtained from reflexion :
MethodInfo createSomeMethodInfo =
typeof(Option<TResult>)
.GetMethod(
"Some",
BindingFlags.Public | BindingFlags.Static);
MethodInfo createNoneMethodInfo =
typeof(Option<TResult>)
.GetMethod(
"None",
BindingFlags.Public | BindingFlags.Static);
And then, in order to finally build a matching expression, I loop over the match conditions and result selector expressions the following way:
private Func<TSource, Option<TResult>> BuildPatternMatchingFunc()
{
ParameterExpression parameter =
Expression.Parameter(typeof(TSource));
Expression noneExpression = Expression.Call(
createNoneMethodInfo);
Expression caseExpression = noneExpression;
foreach (var matchCase in this.cases.Reverse())
{
caseExpression =
Expression.Condition(
Expression.Invoke(
matchCase.TestExpression,
parameter),
Expression.Call(
createSomeMethodInfo,
Expression.Invoke(
matchCase.SelectorExpression,
parameter)),
caseExpression);
}
return Expression
.Lambda<Func<TSource, Option<TResult>>>(
caseExpression,
parameter)
.Compile();
}
The createNoneMethodInfo and createSomeMethodInfo are the MethodInfo corresponding to the generic Option<T>.None() and Option<T>.Some() methods obtained by reflection.
Debugging this kind of expression building can be complicated, particularly without the ability to visualize clearly the expression tree… Hopefully, I have built an expression tree visualizer in the past (I could also have used the easy path and install the classical one) ! From the C# sample again, I can produce the following expression tree:
This tree may seem wide, but is is just a succession of conditional expressions… Finally, the the function that evaluates the result of the pattern-matching expression is the following one:
public TResult Eval(TSource candidate)
{
if (this.patternMatchingFunc == null)
{
this.patternMatchingFunc = BuildPatternMatchingFunc();
}
Option<TResult> matchResult = patternMatchingFunc
.Invoke(candidate);
if (!matchResult.IsSome)
throw new NoMatchFoundException();
return matchResult.Value;
}
As you can see, it’s a wrapper around the expression evaluation invocation. It first generates the expression instantiates upon the first call (in a non-threadsafe way, but who cares !) and handles “nothing matches” case by throwing an exception. And this works !
Now, you might say that matching a single value in of very limited interest. I can’t deny it. Why don’t we try to match several values, then ?

