11# Match Expressions
22
3- The second central idea is the match expression itself. It is represented by two classes: ` Match<TInput, TOutput> `
4- and ` Match<TInput> ` . The difference between them is that the former represents a match expression which yields
5- a result, and the latter represents a match expression which doesn't yield a result (also known as match statement).
3+ The second central idea is the match expression itself. It is represented by two classes: ` Match<TInput, TOutput> ` and
4+ ` Match<TInput> ` . The difference between them is that the former represents a match expression which yields a result, and
5+ the latter represents a match expression which doesn't yield a result (also known as a match statement).
66
77## Using Match Expressions
88
@@ -12,27 +12,27 @@ A match expression can be created using the `Create` methods of the static class
1212
1313### Adding Cases
1414
15- The ` Match ` classes include ` Case ` methods which are used to add a pattern and a function which is executed if
16- the match is successful. Match expressions are immutable - ` Case ` methods return new match expressions; they
17- do not affect the ones on which they are called.
15+ The ` Match ` classes include ` Case ` methods which are used to add a pattern and a function which is executed if the match
16+ is successful. Match expressions are immutable – ` Case ` methods return new match expressions; they do not affect the
17+ ones on which they are called.
1818
19- ` Case ` methods are generic - they also contain information about the pattern's transformation type. Match expressions
19+ ` Case ` methods are generic – they also contain information about the pattern's transformation type. Match expressions
2020can contain patterns of arbitrary transformation types without knowing about these types.
2121
2222### Executing Match Expressions
2323
24- To execute a match expression, the ` ExecuteOn ` method is used. It takes the input value to match. There are two modes
25- of execution in match expressions: strict and non-strict. The strict mode throws an exception if no matches were found,
26- and the non-strict doesn't.
24+ To execute a match expression, the ` ExecuteOn ` method is used. It takes the input value to match. There are two modes of
25+ execution in match expressions: strict and non-strict. The strict mode throws an exception if no matches were found, and
26+ the non-strict doesn't.
2727
28- In the ` Match<TInput, TOutput> ` class the ` ExecuteOn ` method returns the result of the match or throws a
29- ` MatchException ` if no successful match was found. ` Match<TInput, TOutput> ` also contains the ` ExecuteNonStrict `
30- method which executes the match expression in the non-strict mode. It returns ` MatchResult<TOutput> ` because
31- the result might not be present.
28+ In the ` Match<TInput, TOutput> ` class, the ` ExecuteOn ` method returns the result of the match or throws a
29+ ` MatchException ` if no successful match was found. ` Match<TInput, TOutput> ` also contains the ` ExecuteNonStrict ` method
30+ which executes the match expression in the non-strict mode. It returns ` MatchResult<TOutput> ` because the result might
31+ not be present.
3232
33- In the ` Match<TInput> ` class the ` ExecuteOn ` method doesn't return anything, and also throws a ` MatchException `
34- if the match wasn't successful. This class also contains the ` ExecuteNonStrict ` method - it returns a boolean value
35- which indicates whether the match was successful and doesn't throw an exception if it wasn't.
33+ In the ` Match<TInput> ` class, the ` ExecuteOn ` method doesn't return anything, and also throws a ` MatchException ` if the
34+ match wasn't successful. This class also contains the ` ExecuteNonStrict ` method – it returns a boolean value which
35+ indicates whether the match was successful and doesn't throw an exception if it wasn't.
3636
3737The ` ToFunction ` method and its variations are also available. They return a function which, when called, will execute
3838the match expression.
@@ -45,7 +45,7 @@ Fall-through must be explicitly enabled for cases and then explicitly enabled du
4545contain the ` ExecuteWithFallthrough ` method which takes fall-through behavior into account. ` ExecuteOn ` and
4646` ExecuteStrict ` ignore the fall-through behavior.
4747
48- If a case has fall-through enabled, then the expression falls to the _ next successful _ match, unlike ` switch ` , which
48+ If a case has fall-through enabled, then the expression falls to the next successful match, unlike ` switch ` , which
4949falls to the next case whether it's successful or not.
5050
5151The ` Case ` methods are overloaded to accept a boolean value which indicates the fall-through behavior. If fall-through
@@ -54,30 +54,30 @@ the expression will stop at this pattern and not go any further.
5454
5555` Match.Create ` is also overloaded to take the default fall-through behavior.
5656
57- Matching with fall-through is lazy i.e. it returns an ` IEnumerable ` and is only executed when this enumerable
58- is enumerated. Because matching will fall-through is lazy, it doesn't have any modes of execution - the user must
59- decide whether to throw an exception or not if there were no successful matches.
57+ Matching with fall-through is lazy i.e. it returns an ` IEnumerable ` and is only executed when this enumerable is
58+ enumerated. Because matching will fall-through is lazy, it doesn't have any modes of execution – the user must decide
59+ whether to throw an exception or not if there were no successful matches.
6060
61- In the ` Match<TInput, TOutput> ` class the ` ExecuteWithFallthrough ` method returns an ` IEnumerable<TOutput> `
62- which can be used to get all successful match results.
61+ In the ` Match<TInput, TOutput> ` class, the ` ExecuteWithFallthrough ` method returns an ` IEnumerable<TOutput> ` which can
62+ be used to get all successful match results.
6363
64- In the ` Match<TInput> ` class there are no results, so the ` ExecuteWithFallthrough ` method returns
65- an ` IEnumerable<object> ` which should be used simply to enumerate the match process itself. This is implemented
66- so that matching with fall-through is also lazy in this class. The values of the resulting enumerable don't matter -
67- in fact, they are always ` null ` , because match statements don't produce any results. What matters is the process
68- of enumeration.
64+ In the ` Match<TInput> ` class there are no results, so the ` ExecuteWithFallthrough ` method returns an
65+ ` IEnumerable<object> ` which should be used simply to enumerate the match process itself. This is implemented so that
66+ matching with fall-through is also lazy in this class. The values of the resulting enumerable don't matter - in fact,
67+ they are always ` null ` , because match statements don't produce any results. What matters is the process of enumeration.
6968
70- You can use the LINQ's ` Take ` method to limit the number of executed matches, or the ` Count ` method to execute it
71- and get the number of successful matches.
69+ You can use the LINQ's ` Take ` method to limit the number of executed matches, or the ` Count ` method to execute it and
70+ get the number of successful matches.
7271
73- The ` Matchmaker.Linq ` namespace contains the ` Enumerate ` extension method for ` IEnumerable<T> ` which enumerates
74- it and ignores the result. You can use it if you just want to execute the match statement with fall-through.
72+ The ` Matchmaker.Linq ` namespace contains the ` Enumerate ` extension method for ` IEnumerable<T> ` which enumerates it and
73+ ignores the result. You can use it if you just want to execute the match statement with fall-through.
7574
76- ** Remember: matching with fall-through is lazy and is actually executed when the result is enumerated.**
75+ > [ !IMPORTANT]
76+ > Matching with fall-through is lazy and is actually executed when the result is enumerated.
7777
7878Here's a (somewhat convoluted) implementation of the famous fizz-buzz program which uses matching with fall-through:
7979
80- ```
80+ ``` c#
8181using System .Linq ;
8282
8383using Matchmaker ;
@@ -99,7 +99,8 @@ var result = Enumerable.Range(0, 15)
9999 .Select (items => items .Aggregate (String .Concat ))
100100 .ToList ();
101101
102- // The result is ("FizzBuzz", "1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz");
102+ // The result is:
103+ // "FizzBuzz", "1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz"
103104```
104105
105106## Static Match Expressions
@@ -109,7 +110,7 @@ var result = Enumerable.Range(0, 15)
109110One pain point of match expressions is that whenever a method which contains a match expression is executed, the match
110111expression is initialized from scratch. Take a look at this example:
111112
112- ```
113+ ``` c#
113114void DoStuff (int i ) =>
114115 Match .Create <int , string >()
115116 .Case (.. .)
@@ -119,21 +120,21 @@ void DoStuff(int i) =>
119120 .ExecuteOn (i );
120121```
121122
122- The problem here is that if we call ` DoStuff ` 10,000 times, we will initialize the match expression 10,000 times
123- as well, even though it's actually the same expression. Having just 4 cases may not seem like much, to the lag does
123+ The problem here is that if we call ` DoStuff ` 10,000 times, we will initialize the match expression 10,000 times as
124+ well, even though it's actually the same expression. Having just 4 cases may not seem like much, but the lag does
124125accumulate if we execute it thousands of times.
125126
126- We can save the expression in a field and then call the ` ExecuteOn ` method on this field. But this makes the code
127- much less readable because the case definitions are in a different place from the actual execution point.
127+ We can save the expression in a field and then call the ` ExecuteOn ` method on this field. But this makes the code much
128+ less readable because the case definitions are in a different place from the actual execution point.
128129
129130### The Solution
130131
131- There is a way to create ' static match expressions' - expressions which will be initialized only once.
132+ There is a way to create static match expressions – expressions which will be initialized only once.
132133
133- The ` Match ` class contains the ` CreateStatic ` methods which allow the creation of static match expressions.
134- Take a look at the modified example:
134+ The ` Match ` class contains the ` CreateStatic ` methods which allow the creation of static match expressions. Take a look
135+ at the modified example:
135136
136- ```
137+ ``` c#
137138void DoStuff (int i ) =>
138139 Match .CreateStatic <int , string >(builder => builder
139140 .Case (.. .)
@@ -147,14 +148,14 @@ It looks almost the same, except for one difference: the calls to `Case` methods
147148called the build action, which is passed to the ` CreateStatic ` method. Now this match expression will be initialized
148149only once, and its initialization code is in the same place as its execution point.
149150
150- The parameter of the build action has the type ` MatchBuilder<TInput, TOutput ` or ` MatchBuilder<TInput> `
151- depending on which type of match expressions you are building. This type has the same methods for adding cases as
152- the ` Match ` classes and is mutable - the methods return the same builder instance.
151+ The parameter of the build action has the type ` MatchBuilder<TInput, TOutput ` or ` MatchBuilder<TInput> ` , depending on
152+ which type of match expressions you are building. This type has the same methods for adding cases as the ` Match ` classes
153+ and is mutable – the methods return the same builder instance.
153154
154155` MatchBuilder ` also has the ` Fallthrough ` method which specifies the default fall-through behavior. But this method
155156specifies fall-through behavior only for cases that are defined after it. For example:
156157
157- ```
158+ ``` c#
158159builder
159160 .Fallthrough (true )
160161 .Case (.. .) // this case has fall-through behavior if not specified otherwise
@@ -168,14 +169,14 @@ Every case can configure its fall-through behavior individually as well.
168169
169170### Caching Match Expressions
170171
171- The build action will be called only once, and its result will be cached. The cache is a static hash-table.
172- The caching process is not thread-safe.
172+ The build action will be called only once, and its result will be cached. The cache is a static hash-table. The caching
173+ process is not thread-safe.
173174
174175The key of the cache is the place where the ` CreateStatic ` method is called. Apart from the build action, this method
175- also accepts two caller info arguments: the path to the source file and the line in the source file. Users don't
176- need to pass these arguments to the method as they have the ` CallerFilePath ` and ` CallerLineNumber ` attributes.
176+ also accepts two caller info arguments: the path to the source file and the line in the source file. Users don't need to
177+ pass these arguments to the method as they have the ` CallerFilePath ` and ` CallerLineNumber ` attributes.
177178
178- The ` Match ` class also contains the ` ClearCache ` methods which clear a global cache. Match expressions have a cache
179- per type (so ` Match<int, int> ` uses a different cache than ` Match<int, string> ` ) so ` ClearCache ` only clears one
180- cache. Clearing the cache will force all static match expressions of that type to be reinitialized. This process is
181- not thread-safe as well.
179+ The ` Match ` class also contains the ` ClearCache ` methods which clear a global cache. Match expressions have a cache per
180+ type (so ` Match<int, int> ` uses a different cache than ` Match<int, string> ` ) so ` ClearCache ` only clears one cache.
181+ Clearing the cache will force all static match expressions of that type to be reinitialized. This process is not
182+ thread-safe as well.
0 commit comments