Skip to content

Proposal: List, Indexer (Dictionary) and String Patterns #5811

Closed
@alrz

Description

@alrz

Following patterns are based on the syntax proposed in #206.

List Patterns

List patterns can be used on arrays and all types that have implemented the IList interface . It would iterate through the list and match the list-subpatterns.

Syntax

list-pattern:
{ list-subpatterns }
{ }

list-subpatterns:
 list-subpattern
 list-subpatterns
, list-subpattern

list-subpattern:
 pattern
 sequence-wildcard-pattern

sequence-wildcard-pattern:
**
***

Remarks

  • { } matches an empty list. Determined by Enumerable.Any or ICollection.Count based on type.
  • ** matches any sequence of one or more items.
  • *** matches any sequence of zero or more items.
  • To keep the rules simple, sequence wildcards can be used only one time in the whole pattern.
  • At runtime, considering sequence wildcards, it first calculates the minimum/maximum/exact length of the list, if it matches then it starts to iterate.

EDIT 1: Sequence wildcards are capable to be used also in tuple patterns.

switch(tuple) {
  case (true, **): ...
}

It would match a tuple with any length equal or greater than two.

EDIT 2: It would be nice to be able to use slices (#120) to catch the "rest" of the list at once, e.g.

arr is { int first, .. int[:] rest }

// equivalent to
if(arr is { int first, *** }) {
  rest[:] = arr[1:]; // skips the first item
}

Examples

switch(array) {
    // matches an empty array
    case { }:           
    // matches an array with exact length of two
    case { var i1, var i2 }:           
    // matches an array of tuples with minimum length of two and returns the first two item
    // with the first item deconstructed into t1 and t2
    case { (var t1, var t2) , var i2, *** }:           
    // matches an array with the minimum length of three,
    // picks the last three item and skips the one before the last
    case { *** , var i1, *, var i2 }: 
    // matches an array with the minimum length of three, returns the first and last item
    case { var i1, ** , var i2 }:
}

Indexer Patterns

Indexer patterns can be used on all types with an indexer, just like dictionary initializers.

Syntax

indexer-pattern:
{ indexer-subpatterns }

indexer-subpatterns:
 indexer-subpattern
 indexer-subpatterns
, indexer-subpattern

indexer-subpattern:
[ constant-expression-list ] is pattern

An indexer pattern matches the values extracted by the use of indexer with the specified arguments. Following the property pattern syntax, we use an is keyword instead of = sign. The whole match fails only if the one of indexer subpatterns fail. The order in which subpatterns are matched is not specified, and a failed match may not match all subpatterns. It's a compiler-error if type of the expression _e_ have not defined an indexer with parameters of type constant-expression-list, hence it's pattern incompatible.

EDIT: The indexer pattern syntax can be mixed with property-pattern so one can check a indexer and a property in the same pattern (analogous to object initializers).

String Patterns

String patterns can be considered as inverted string interpolation, and of course they can be combined with regular expression literals (#5806):

var str = "str123";
if(str is ~$"[a-z]+{int number:\d+}") {
    Debug.Assert(number == 123);
}

Behind the scene, this creates a regular expression group at the interpolating section ({ }) and applies the formatting part (\d+) to it, when the regex succeed, then it tries to convert the group capture via TypeConverter back to the variable type. If regular expression doesn't match or conversion wasn't successful, pattern fails.

Default type for variables in string patterns is System.String. The syntax for string patterns is pretty much the same as string interpolation, but formatting part after colon has a different meaning. This can be extended to bind repeating captures to an array or IEnumerable.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions