Chapter 20 Patterns and Pattern Matching
Patterns pat describe structures of values, and are used to bind variables values through pattern matching mechanism, built-in val-bind declarations (valBind)23.1, function declarations (funDecl)23.2, case expressions, fn expression,and handle expressions.
When evaluated, pattern matching generates static type environment at compile time, and dynamic value environment at runtime. These environments are used to added to the evaluation environment for the expressions and declarations in the scope of the pattern.
Syntax of Patterns is given below.
-
•
top-level pattern
pat ::= atpat op longVid atpat data structure pat vid pat data structure (infix operator form) pat : ty pattern with type constraint vid : ty as pat layered pattern -
•
atomic patterns
atpat ::= scon constant _ anonymous pattern vid variable and constructor longVid constructor {patrow } record pattern () unit type constant (pat,,pat) tuples () [pat,,pat] list () (pat) patrow ::= ...
anonymous field lab = pat , patrow record fields vid:ty as pat ,patrow label and variable
The following subsections define for each pattern pat, its type ty, matching values, and the resulting type environment. The dynamic environment generated at runtime corresponds to the generated type environment and it binds identifiers to the matched values.
Constant pattern:scon
They are the same the constant expressions defined in 19.2. They have the corresponding types, match the same constant values, and generate the empty type environment.
Anonymous pattern:_
This has arbitrary type determined by the context, matches any value, and generates the empty type environment.
identifier pattern:vid
If the identifier is defined as a constructor, then it has the type of the constructor, matches the constructor, and generate the empty type environment.
If the identifier is not defined or defined as a variable, then it has arbitrary type ty determined by the context, matches any value of type ty, and generate the type environment .
The following shows simple examples.
SML# 4.0.0 (2021-04-06) for x86_64-pc-linux-gnu with LLVM 11.1.0
# val A = 1
val A = 1 : int
# fn A => 1;
val it = fn : [’a. ’a -> int]
# datatype foo = A;
datatype foo = A
# fn A => 1;
val it = fn : foo -> int
# datatype foo = A of int;
datatype foo = x of int
# fn A => 1;
(interactive):12.3-12.3 Error:
(type inference 039) data constructor A used without argument in pattern
long identifier:longVid
If the long identifier is defined as a constructor, then it has the type of the constructor, matches the constructor, and generate the empty type environment. It is an error if the identifier is not defined as a constructor or defined as a constructor with an argument. The following shows simple examples.
# structure A = struct datatype foo = A | B of int val C = 1 end;
structure A =
struct
datatype foo = A | B of int
val C = 1 : int
end
# val f = fn A.A => 1;
val f = fn : A.foo -> int
# val g = fn A.B => 1;
(interactive):3.11-3.13 Error:
(type inference 046) data constructor A.B used without argument in pattern
# val h = fn A.C => 1;
(interactive):4.11-4.13 Error: (name evaluation "020") unbound constructor: A.C
A.B is a constructor with an argument and A.C is a variable, function declarations g and h result in errors.
record pattern:{patrow }
The pattern {} without record fields has the unit type, matches the value (), and generate the empty type environment.
If it has of the form lab:ty,, lab:ty, with non-empty record fields, there are two cases according to the record field pattern patrow. Let be the type environment generated by the set of patterns in the fields.
-
1.
Monomorphic record pattern, i.e. the case where the anonymous field
...
is not contained. It has the (monomorphic) record type {lab:ty,, lab:ty}, matches any records containing all the fields that matches the record field patterns patrow, and generate . -
2.
Polymorphic record pattern, i.e. the case where the anonymous field
...
is contained. It has the polymorphic record type ’a#{lab:ty,, lab:ty} with the record kind of the field types of patrow, matches any records containing all the fields that matches the record field patterns patrow, and generate . The polymorphic type of this entire pattern can be instantiated with any record types having the kind.
record field pattern:patrow
-
•
Anonymous field pattern : .... It indicates that matching records may contains more fields than the specified fields.
-
•
Field pattern : lab = pat , patrow. Let the filed type and the generated type environment of , patrow be lab:ty,, lab:ty, and . Let the type and the generated type environment of the pattern pat be ty and . If the label lab is different than any labels lab,,lab, then the type and the generated type environment of the entire pattern are lab:ty,lab:ty,, lab:ty, and . It is a type error if the label lab is one of lab,,lab.
-
•
variable as label pattern: vid:ty as pat , patrow.
This is converted to the following field pattern before evaluation.
vid = vid:ty as pat , patrow.
The generated static environment and the matching dynamic value are the same as the transformed record pattern. The following shows simple examples.
# val f = fn {x:int as 1, y} => x + y;
(interactive):33.8-33.34 Warning: match nonexhaustive
{x = x as 1, y = y} => ...
val f = fn : {x: int, y: int} -> int
# val g = fn {x = x:int as 1, y = y} => x + y;
(interactive):34.8-34.37 Warning: match nonexhaustive
{x = x as 1, y = y} => ...
val g = fn : {x: int, y: int} -> int
# f {x = 1, y = 2};
val it = 3 : int
# g {x = 1, y = 2};
val it = 3 : int
Tuple pattern:(pat,,pat)
This is converted to the monomorphic record pattern
{1=pat,,n=pat}.
The generated static environment and the matching dynamic value are the same as the transformed record pattern. The following shows simple examples.
# val f = fn (x,y) => 2 * x + y;
val f = fn : int * int -> int
# val g = fn {1=x, 2=y} => 2 * x + y;
val g = fn : int * int -> int
# f 1=1, 2=2;
val it = 4 : int
# g (1,2);
val it = 4 : int
List pattern:[pat,,pat]
This is converted to the following nested list data structure pattern:
pat :: :: pat :: nil
The generated static environment and the matching dynamic value are the same as the transformed constructor application pattern. The following shows simple examples.
# val f = fn [x,y] => 2 * x + y;
(interactive):24.8-24.26 Warning: match nonexhaustive
:: (x, :: (y, nil )) => ...
val f = fn : int list -> int
# val g = fn (x::y::nil) => 2 * x + y;
(interactive):25.8-25.32 Warning: match nonexhaustive
:: (x, :: (y, nil )) => ...
val g = fn : int list -> int
# f (1::2::nil);
val it = 4 : int
# g [1,2];
val it = 4 : int
Data structure pattern:op longVid atpat
If longVid is bound to a constructor of type of the form ty -> ty,then it has type ty and generates a type constructor generated by atpat. This pattern matches a data structure of the form if the subterm matches atpat.
Data structure patter in infix form pat vid pat is syntactically converted to op vid(pat,pat) before evaluation. The type and type environment being generated and the matching dynamic value are the same as those of converted pattern.
Typed pattern: pat:ty
If pattern pat has type ty ad type environment , and ty and ty unifies under type substitution , then this pattern has type and type environment . This pattern matches a value of type that matches pattern pat.
Layered pattern: vid : ty as pat
If the pattern pat:ty has type ty and a type environment then this pattern has type ty and a type environment . It matches a dynamic value that the pattern pat:ty matches.