This expresses general conditional branches on the dynamically-typed value of ⟨exp⟩ . ⟨match⟩ is of the form:
⟨pat1⟩ => ⟨exp1⟩
| · · ·
| ⟨patn⟩ => ⟨expn⟩
The type of each pattern may differ from each other. Variable patterns and anonymous patterns occurring in ⟨pati⟩ must be type-annotated. In addition, the set of patterns must satisfy the following restriction:
• When categorizing the set of patterns by their types, for each set of patterns of the same type, the set of patterns must satisfy the same condition as the case expression.
For example, the following is redundant on int patterns and therefore violates the rule.
# fn x => _dynamiccase x of x:int => "int" | x:real => "real" | 0:int => "zero";
(interactive):1.8-1.76 Error: match redundant x => ...
--> 0 => ...
19.25. CASE BRANCH EXPRESSION WITH DYNAMIC TYPE CAST _DYNAMICCASE ⟨EXP⟩ OF ⟨MATCH⟩ 117 This expression is evalated as follows. It tries to match the dynamically-typed value v of ⟨exp⟩ with
each pattern from ⟨pat1⟩ to ⟨patn⟩ in this order. Before trying matching with a pattern, v is casted to the type of the pattern in the same way as the _dynamic expression. For the first matched pattern
⟨pati⟩ , it binds variables in ⟨pati⟩ to their corresponding part of the value and evaluates ⟨expi⟩ to the result value. If no dynamic type cast succeeds, it raises the Dynamic.RuntimeTypeError exception. If no pattern matches with the value, it raises the Match exception.
To use this expression in the separate compilation mode, "reify.smi" must be _required.
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
| (⟨pat1⟩ ,· · ·, ⟨patn⟩ ) tuples (n≥ 2)
| [⟨pat1⟩ ,· · ·, ⟨patn⟩ ] list (n≥ 0)
| (⟨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 re-sulting 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.
119
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 { ⟨vid⟩ : ⟨ty⟩ }.
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
(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.
(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 ⟨lab1⟩ : ⟨ty1⟩ ,. . ., ⟨labn⟩ : ⟨tyn⟩ ,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 {⟨lab1⟩ : ⟨ty1⟩ ,. . ., ⟨labn⟩ : ⟨tyn⟩ }, matches any records contain-ing 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#{⟨lab1⟩ : ⟨ty1⟩ ,. . ., ⟨labn⟩ : ⟨tyn⟩ } 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.
121 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 ⟨lab1⟩ : ⟨ty1⟩ ,. . ., ⟨labn⟩ : ⟨tyn⟩ , and Γ. Let the type and the generated type environment of the pattern ⟨pat⟩ be ⟨ty⟩ and Γ0. If the label ⟨lab⟩ is different than any labels
⟨lab1⟩ ,. . ., ⟨labn⟩ , then the type and the generated type environment of the entire pattern are
⟨lab⟩ : ⟨ty⟩ , ⟨lab1⟩ : ⟨ty1⟩ ,. . ., ⟨labn⟩ : ⟨tyn⟩ ,and Γ0∪ Γ. It is a type error if the label ⟨lab⟩ is one of ⟨lab1⟩ ,. . ., ⟨labn⟩ .
• 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:(⟨pat1⟩ ,· · ·, ⟨patn⟩ ) This is converted to the monomorphic record pattern {1= ⟨pat1⟩ ,· · ·,n= ⟨patn⟩ }.
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:[⟨pat1⟩ ,· · ·, ⟨patn⟩ ] This is converted to the following nested list data structure pattern:
⟨pat1⟩ :: · · · :: ⟨patn⟩ :: 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 C of type of the form ⟨ty1⟩ -> ⟨ty2⟩ ,then it has type ⟨ty2⟩ and generates a type constructor generated by ⟨atpat⟩ . This pattern matches a data structure of the form C(v) if the subterm v matches ⟨atpat⟩ .
Data structure patter in infix form ⟨pat1⟩ ⟨vid⟩ ⟨pat2⟩ is syntactically converted to op ⟨vid⟩ ( ⟨pat1⟩ , ⟨pat2⟩ ) 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 ⟨ty1⟩ ad type environment Γ0, and ⟨ty1⟩ and
⟨ty⟩ unifies under type substitution S, then this pattern has type S( ⟨ty⟩ ) and type environment S(Γ0).
This pattern matches a value of type S(⟨ty⟩ ) 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 Γ ∪ {x : ⟨ty′⟩ }. It matches a dynamic value that the pattern ⟨pat⟩ : ⟨ty⟩ matches.
Chapter 21
Scope rules for identifier
This chapter defines the static scope rules for names used in programs.
As defined in Section 17.2, identifiers are used as names of the following seven classes.
identifier name class
⟨vid⟩ variable and constructor names
⟨strid⟩ structure name
⟨sigid⟩ signature name
⟨funid⟩ functor name
⟨tycon⟩ type constructor name
⟨tyvar⟩ type variable name
⟨lab⟩ record label
Among them, type variable names have the syntax ’..., and are distinguished from the other classes of names. All the other names overlap one another. For example, A can be used as a name of any of the classes other than type variable name. As defined in Chapter 21,the syntax is defined so that the name class of identifier occurrence is uniquely determined. Furthermore, these name classes are managed as separate name spaces, and same identifier can be used as names of different classes. The following example shows usage of the same identifier A as different names.
(* 1 *) val A = {A = 1}
(* 2 *) type ’A A = {A: ’A}
(* 3 *) signature A = sig val A : int A end
(* 4 *) functor A () : A = struct val A = {A = 1} end (* 5 *) structure A : A = A()
(* 6 *) val x = A.A (* 7 *) val y = A : int A
The first occurrence of A in line 7 refers to the variable defined in line 1 and the second occurrence of A refers to the type constructor defined in line 2. The first occurrence of A in line 6 refers to the structure defined in line 5.
Names other than record labels are defined by program constructs and referenced. The following are the program constructs involving name definitions.
1. Interface files in separate compilation. ProvideList ⟨provideList⟩ in the interface file define names. For example, the interface file containing the following declarations
_require "myLibrary.smi"
val x : int
datatype foo = A of int | B of bool
defines variable name x, data constructor names A,B,and type constructor name foo.
2. Structure declaration. The structure name and the set of long names obtained from the set of long names defined in the structure by prefixing the structure name are defined. For example, if the structure S define a set of long names L then a declaration structure S = S defines the structure name S and the set of long names{S.path | path ∈ L}.
123
3. Functor declaration. The functor name and the names in the argument specification are defined.
For example, functor F(type foo) = · · · defines functor name F and type constructor name foo.
4. Type alias declaration. The type constructor name and the argument type variable names are defined. For example, type foo = · · · defines type constructor (without type parameter) name foo.
5. Datatype declaration. The type constructor name, the argument type variable names and the data constructor names are defined. For example, datatype ’a foo = A of int | B of bool defines type constructor name foo,argument type variable name ’a,and data constructor names A,B. .
6. Exception declarations. Exception constructor names are defined. For example, exception E defines exception constructor (without argument) name E.
7. Val declaration.The variables occurring in the bound pattern are defined. For example, if x is not defined as a data constructor, val x = 1 defines variable name x.
8. Function declaration.Function names and variables occurring in argument patterns are defined.
For example, if x is not defined as a data constructor in the occurring context, then fun f x = 1 defines variable name f.
9. fn expression.The variables occurring in argument patterns are defined. For example, if x is not defined as a data constructor in the occurring context, then fn x => x defines variable name f.
10. case expression.The variables occurring in case patterns are defined. For example, if x is not defined as a data constructor in the occurring context, then case y of A x => x defines variable name x.
11. SQL expression.SQL expressions that begins with _sql may contain variable definitions. They are defined in Chapter 22.
These defined names have their scopes (the extents where the defined names can be referenced). These scopes of defined names are nested according to the inductively defined syntax. As a block-structured language in the Algol family, SML# adopts static scoping rules, under which definition and reference relation is determined at compile time, and new definition hides the old definition of the same name in the syntax introduced by the definition.
In the following, we list the static scopes introduced by scope delimiting syntactic constructs. The actual scope of a name is the parts of the static scope of its defining syntactic construct that exclude the inner static scopes of the same name of the same class.
• Separate compilation unit.
A separate compilation unit is a single source file ⟨srcFule⟩ .sml.
The scope of the names defined in a top-level declaration of ⟨srcFile⟩ .sml is the rest of the file.
An interface file reference from a source file also defines names. Without explicit _interface declaration in a source file ⟨srcFile⟩ .sml, the file ⟨srcFile⟩ .sml in the same directory is implicitly referenced as its interface file. The scope of the names defined by the reference to an interface file
⟨smiFilePath⟩ .smi from a source file ⟨srcFile⟩ .sml is the entire source file. The name defined by a reference to an interface file ⟨smiFilePath⟩ .smi is the set of all names declared in the interface files required by _require declarations in ⟨smiFilePath⟩ .smi.
• Structure construction : struct ⟨decl⟩ end.
The scope of a name defined in a top-level declaration in ⟨decl⟩ is the rest of its declaration occurrence of ⟨decl⟩ .
• Local declaration : local ⟨decl1⟩ in ⟨decl2⟩ end.
The scope of a name defined in ⟨decl1⟩ is the rest of its declaration occurrence in ⟨decl1⟩ and the entire ⟨decl2⟩ . The scope of a name defined in ⟨decl2⟩ is the rest of its declaration occurrence in
⟨decl2⟩ .
125
• Let expression : let ⟨decl⟩ in ⟨exp⟩ end.
The scope of a name defined in ⟨decl1⟩ is the rest of its declaration occurrence in ⟨decl⟩ and ⟨exp⟩ .
• Function declaration.
It has the following syntax.
fun ⟨id⟩ ⟨pat1,1⟩ · · · ⟨pat1,n⟩ = ⟨exp1⟩ ..
.
| ⟨id⟩ ⟨patm,1⟩ · · · ⟨patm,n⟩ = ⟨expm⟩
The scope of the function name ⟨id⟩ is the list of ⟨exp1⟩ to ⟨expm⟩ . The scope of names in each pattern ⟨pati,j⟩ is the corresponding expression ⟨expi⟩ .
• Function expression.
It has the following syntax.
fn ⟨pat1⟩ => ⟨exp1⟩ | · · · | ⟨patn⟩ => ⟨expm⟩
The scope of a name defined in a pattern ⟨pati⟩ is the corresponding expression ⟨expi⟩ .
• Case expression.
It has the following syntax.
case exp of ⟨pat1⟩ => ⟨exp1⟩ | · · · | ⟨patn⟩ => ⟨expm⟩
The scope of a name in each pattern ⟨pati⟩ is the corresponding expression ⟨expi⟩ .
Chapter 22
SQL Expressions and Commands
SML# includes database queries compliant to the standard SQL as SML# expressions. The SQL queries and their fragments are first-class citizens; therefore, SQL queries as well as SML# expressions have SML# types and are typechecked under the type system of SML#. In addition, SQL queries can be freely combined with any other SML# constructs as long as their types are consistent. For example, you can construct SQL query fragments as data structures, keep them in variables or function arguments, and make a complete SQL query by combining them at runtime.