Patterns (ref) - vilinski/nemerle GitHub Wiki

<< Back to Reference Manual.

Table of 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.

pattern =
  constructor_pattern
| throw_away_pattern
| variable_pattern
| record_pattern
| as_binding
| type_check_pattern
| tuple_pattern
| list_constructor_pattern
| list_literal_pattern
| literal_pattern
| type_enforcement_in_pattern

Constructor pattern

constructor_pattern =
qualified_identifier [ '(' 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

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

variable_pattern = IDENTIFIER

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

record_pattern =
qualified_identifier 'where'
'(' IDENTIFIER '=' pattern { ',' IDENTIFIER '=' 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

as_binding =
pattern 'as' IDENTIFIER

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

type_check_pattern =
IDENTIFIER 'is' type

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

tuple_pattern =
'(' pattern { ',' 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

pattern '::' pattern```

Equivalent to:
```nemerle
list.Cons (pattern, pattern)

List literal pattern

list_literal_pattern =
'[' [ { pattern ',' } pattern [ ',' ] ] ']'

Equivalent to:

list.Cons (pattern1, list.Cons (pattern2, ... list.Cons (pattern2, list.Nil) ... ))

Literal pattern

literal

This pattern matches a specified constant value.

Type enforcement in pattern

type_enforcement_in_pattern =
pattern ':' type

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.

⚠️ **GitHub.com Fallback** ⚠️