SML# Document Version 4.1.0
23 Declarations of the core language and their interfaces

23.1 val declarations : valDecl

Syntax of val declarations is given below.

valDecl ::= val tyvarSeq valBind
valBind ::= valBind1
 | valBind1 and valBind
valBind1 ::= pat = exp

In this declaration, the variables appearing in patterns valBind must be distinct. These variables are simultaneously defined, whose scope is the entire valDecl declaration and the declarations that follows.

The type variable declaration tyvarSeq in valDecl delimit their scope. These type variables must be generalized at the top-level of each valBind1 declaration.

23.1.1 val declaration interface : valSpec

For each variable defined in a val declaration, the following form of interface specification must be declared.

valBindInterface ::= val id : ty

For example,for val declaration

val (x,y) = (1,2)

the following two interface specification must be given.

val x : int
val y : int

23.1.2 val declaration evaluation

Val declaration of the form

val pat1 = exp1  and pat2 = exp2

is evaluated in the following two steps.

Structured pattern evaluating

A val declaration with a structured pattern pat is first transformed to sequence of val declarations for the set {x1,,xm} of variables in the pattern pat.

If the pattern pat does not contain constructor or constant, then the transformation is done by recursively decompose pati and expi pair to a sequence of pairs of a sub-pattern and a sub-expression. This is done in the following steps.

  1. 1.

    Each pattern pati is decomposed into sub-patterns according to the structure of pati. When pati has a type constraint, then the corresponding type constraint is attached to each of the decomposed sub-patterns.

  2. 2.

    For each decomposed sub-pattern of pati, the corresponding sub-expression is generated from the expression expi by applying the code to extract the corresponding value to expi.

  3. 3.

    If a sub-pattern is a layered pattern of the form id as pat, the additional code to bind id to the entire value is generated.

  4. 4.

    Finally, the following from of val declarations is generated from the sequence of pairs of a variable and an expression obtained from the above transformation steps.

    val x1 (:τ1)? = exp1
      
    and xm (:τm)? = expm

This decomposition process enables the variables in structured patterns to have rank-1 polymorphic types as far as possible. However, the above transformation cannot be applied to val declarations with constructors and constants, since these val declarations may raise exception at runtime. A val declaration containing constructors and constants is transformed to the following val declaration with a variable.

val X = case (exp1, , expn) of
          (
pat1, , patn) => (x1,,xm)
        | _ => raise Bind
val x1 = #1 X
  
and xm = #m X

23.1.3 Example of val declarations and interface

The following are simple examples in the interactive mode.

# val (x,y) = (print "SML#\n", fn x => fn y => (x,y));
SML#
val x = () : unit
val y = fn : [’a. ’a -> [’b. ’b -> ’a * ’b]]
 
# val (z, w, 1) = (print "SML#\n", fn x => fn y => (x,y), 1);
(interactive):2.8-2.8 Warning:
  (type inference 065) dummy type variable(s) are introduced due to value
  restriction in: y
(interactive):2.4-2.57 Warning: binding not exhaustive
  (x, y, 1) => ...
SML#
val z = () : unit
val w = fn : fn : ?X7 -> ?X6 -> ?X7 * ?X6

The following is a simple example of a source file and an interface file in separate compilation.

Version.sml file: val (version, releaseDate) = ("4.1.0", "2021-11-08")
Version.smi file: val version : string
val releaseDate : string