Patterns (ref)
From Nemerle Homepage
Contents |
Patterns
Patterns are a form of accessing data structures, especially trees. Patterns can match values. A definition of the term to match is given with each pattern construct. However, the main idea behind patterns is that they match values that look like them.
Pattern are used in match expression and value definitions.
Constructor pattern
The identifier should refer to the name of variant option. This pattern
matches a value if it is a specified variant option, and sub-pattern
matches variant option payload.
Example:
variant Color { | Red | Yellow | Green | Rgb { red : byte; green : byte; blue : byte; } | Alpha { opacity : byte; color : Color } override public ToString() : string { match (this) { | Red => "<Red>" // you can ommit type prefix (Color.) if expression type inferred before typing "match" operator | Color.Yellow => "<Yellow>" | Green() => "<Green>" | Rgb(0, 0, 0) => "<Black>" | Rgb(0xFF, 0xFF, 0xFF) => "<White>" | Rgb(red, green, blue) => $"Color.Rgb($red, $green, $blue)" | Alpha(0, _) => "<transparent>" | Alpha(opacity, color) => $"Color.Alpha($opacity, $color)" } } } WriteLine(Color.Green()); WriteLine(Color.Rgb(0, 0, 0)); WriteLine(Color.Rgb(255, 255, 255)); WriteLine(Color.Alpha(55, Color.Rgb(255, 255, 255))); WriteLine(Color.Alpha(0, Color.Yellow()));
Console output:
<Green> <Black> <White> Color.Alpha(55, <White>) <transparent>
Throw-away pattern
This pattern matches any value.
Example:
def randomizer = System.Random(42); repeat (5) match (randomizer.Next(0, 2)) { | 0 => WriteLine("zero"); | _ => WriteLine("other number"); }
Console output:
other number zero zero other number zero
Variable pattern
This pattern matches any value and bind it with varable (new local immutable variable).
The scope of this variable is action.
Example:
def randomizer = System.Random(42); repeat (5) match (randomizer.Next(0, 2)) { | 0 => WriteLine("zero"); | x => WriteLine(x); }
Console output:
1 zero zero 1 zero
Record pattern
This pattern matches a value of a class, that has all specified fields
(this is checked statically), and a value of each field matches respective
pattern. This pattern can be used to match a value of a variant, but it not a good way.
Example:
class A { } [Record] class B : A { public Field1 : int; public Property1 : string { get; set; } } [Record] class C : A { public Field2 : string; public Property2 : int { get; set; } } def match_object(a : A) : void { match (a) { | B where(Field1=42, Property1=x) => WriteLine($"B(42!!!, '$x')") | B where(Field1=x, Property1=y) => WriteLine($"B($x, '$y')") | C where(Field2=x, Property2=y) => WriteLine($"C($x, '$y')") | _ => WriteLine("object of A type or inheritor") } } match_object(B(42, "test 1")); match_object(C("test 2", 1234));
Console output:
B(42!!!, 'test 1') C(test 2, '1234')
As binding
This pattern matches the same value as an enclosed pattern does. However,
in addition the value matched by the enclosed pattern is bound to a
specified variable, which can be used in when guard or match body.
Example:
variant Color { | Red | Yellow | Green | Rgb { red : byte; green : byte; blue : byte; } | Alpha { opacity : byte; color : Color } override public ToString() : string { ... } } def color : Color = Color.Alpha(0, Color.Rgb(0, 0, 0)); match (color) { | Alpha(0, Color.Rgb as rgb) => WriteLine($"transparent RGB value: $rgb") | _ => WriteLine(color) }
Console output:
transparent RGB value: <Black>
Type check pattern
This pattern matches a value if it possesses the given type. In addition,
the value matched is bound to a specified variable, which gets the
given type.
This pattern can be used both for checking the type and hinting the type checker (if the value is statically known to always have given type, compiler issues a warning and no runtime checks are performed).
Example:
def sort[T](seq : IEnumerable[T]) : IEnumerable[T] { WriteLine($"\nseq is $(seq.GetType().Name)"); match (seq) { | ary is array[T] => Array.Sort(ary); ary | lst is System.Collections.Generic.List[T] => lst.Sort(); lst | lst is list[T] => lst.Sort((x, y) => (x :> IComparable[T]).CompareTo(y)) | _ => seq.OrderBy(x => x) } } def reult = sort([3, 1, 4, 9, 2]); WriteLine($"..$reult"); def reult = sort(array[3, 1, 4, 9, 2]); WriteLine($"..$reult"); def reult = sort(List([3, 1, 4, 9, 2])); WriteLine($"..$reult");
Console output:
seq is Cons 1, 2, 3, 4, 9 seq is Int32[] 1, 2, 3, 4, 9 seq is List`1 1, 2, 3, 4, 9
Tuple pattern
This pattern matches a tuple with specified contents (each tuple member
is matched be respective pattern).
In addition, when a tuple pattern is seen, where a record pattern would be otherwise expected -- the tuple pattern is transformed to record pattern by adding field identifiers in order they appear in the definition of the given class. A tuple pattern transformed to a record pattern cannot match fields inherited from the base class.
Example:
type Line = bool * bool * bool; def print(tic_tac_toe : Line * Line * Line) { match (tic_tac_toe) { | ((x1y1, x1y2, x1y3), (x2y1, x2y2, x2y3), (x3y1, x3y2, x3y3)) => def cell(x) { if (x) " X" else " 0" } WriteLine(cell(x1y1) + cell(x1y2) + cell(x1y3)); WriteLine(cell(x2y1) + cell(x2y2) + cell(x2y3)); WriteLine(cell(x3y1) + cell(x3y2) + cell(x3y3)); } } def isXWin(tic_tac_toe : Line * Line * Line) { match (tic_tac_toe) { | ((true, true, true), _, _) | ( _, (true, true, true), _) | ( _, _, (true, true, true)) | ((true, _, _), (true, _, _), (true, _, _)) | ((_, true, _), (_, true, _), (_, true, _)) | ((_, _, true), (_, _, true), (_, _, true)) | ((true, _, _), ( _, true, _), ( _, _, true)) | (( _, _, true), ( _, true, _), (true, _, _)) => true | _ => false } } def test_tic_tac_toe(tic_tac_toe) { WriteLine(); print(tic_tac_toe); def result = isXWin(tic_tac_toe); WriteLine($"Is 'X' win? $result") } test_tic_tac_toe( (( true, false, false), (false, true, false), (false, false, true)) ); test_tic_tac_toe( (( true, false, false), (false, true, false), (false, false, false)) );
Console output:
X 0 0 0 X 0 0 0 X Is 'X' win? True X 0 0 0 X 0 0 0 0 Is 'X' win? False
List constructor pattern
Equivalent to:
list.Cons (pattern, pattern)
List literal pattern
Equivalent to:
list.Cons (pattern1, list.Cons (pattern2, ... list.Cons (pattern2, list.Nil) ... ))
Literal pattern
This pattern matches a specified constant value.
Type enforcement in pattern
This construct is used to statically enforce type of subpattern to given
type. If a matched expression might not have such a type, compiler
issues an error. It is used mainly for hinting type inference engine
and improving code clarity.