SML# Document

Chapter 20. Scope rules for identifier

This chapter defines the static scope rules for names used in programs.

As defined in Section 16.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 20,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 | pathL}.

  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 AB. .

  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 of the form _sql db => exp contain variable definitions. They are defined in Chapter 21.

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.

  • Let expression : let decl1 in exp end.

    The scope of a name defined in decl1 is the rest of its declaration occurrence in decl1 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.