プログラミング言語SML#解説 4.0.0版
16 SML#の構造

16.1 対話型モードのプログラム

対話型モードのSML#プログラムは,セミコロン(;)で終 了する宣言の列である. 以下はインタラクティブセッションの例である.

$ smlsharp
SML# 4.0.0 ...
# fun fact 0 = 1
>    | fact n = n * fact (n - 1);
> val x = fact 10;
val fact = fn : int -> int
val x = 3628800 : int

ここで,#>は,対話型SML#コンパイラが印字 する先頭行及び継続業のプロンプト文字である. この例のように,対話型コンパイラは,入力された宣言の評価結果を表示する.

宣言には,関数やレコードなどの値を生成するプログラムである核言語の 宣言declと,それら宣言をひとまとまりにして名前をつけるモジュー ル言語の宣言topDeclに大別される.

interactiveProgram ::= ;
 | decl interactiveProgram
 | topdecl interactiveProgram
  • 核言語の宣言

    以下に核宣言 (decl) の構造と簡単な例を示す.

    decl ::= infixDecl infix 宣言
     | valDecl val 宣言
     | valRecDecl val rec宣言
     | funDecl 関数宣言
     | datatypeDecl データ型宣言
     | typeDecl 型の別名宣言
     | exceptionDecl 例外宣言
     | localDecl 局所宣言
    宣言の種類 簡単な例
    infix 宣言 infix 4 =
    val 宣言 val x = 1
    val rec 宣言 val rec f = fn x => if x = 0 then 1 else x * f (x - 1)
    関数宣言 fun f x = if x = 0 then 1 else x * f (x - 1)
    データ型宣言 datatype foo = A | B
    型宣言 type person = {name:string, age:int}
    例外宣言 exception Fail of string
    局所宣言 local val x = 2 in val y = x + x end
  • モジュール言語の宣言

    以下にモジュール宣言 (moduleDecl) の構造と簡単な例を示す.

    topdecl ::= strDecl ストラクチャ宣言
     | sigDecl シグネチャ宣言
     | functorDecl ファンクタ宣言
     | localTopdecl 局所宣言
    ストラクチャ宣言 structure Version =
      struct
        val version = "4.0.0"
      end
    シグネチャ宣言 signature VERSION =
      sig
        val version :string
      end
    ファンクタ宣言 functor System(V:VERSION) =
      struct
        val name = "SML#"
        val version = V.version
      end
    局所宣言 local
      structure V = Version
    in
      structure Release = struct
        val version = V.version
        val date = "2021-04-06"
      end
    end

16.1.1 核言語の宣言の評価

対話型プログラムの実行は,宣言列を順次評価することによって行われる. 核言語の宣言の評価は,宣言に含まれる式などの要素を評価し,値を生 成し,宣言で定義される名前をそれら値に束縛する効果を持つ. 生成される値には,静的な値と,実行時に生成される動的な値がある.

静的な値は,コンパイラがコンパイル時に作り出す値であり,以下の種類がある.

中置演算子属性

識別子が,中置演算子として構文解析されることを表す. 結合の向き(右結合,左結合)と強さ(0から9)をもつ.

コンストラクタ属性

識別子がデータコンストラクタであることを表す. コンストラクタ属性を持つ識別子は,パターンマッチングにおいて,対 応するコンストラクタとのみマッチする.

型構成子

型構成子は,datatype宣言で生成される新しい型の定義である. 型引数を持つパラメタ型を定義できる.

型は,対応するプログラム部分が実行時に生成する動的な値の性質 を表現する.

コンパイラは,コンパイル時に宣言に含まれる構成要素が持つ静的な値 を生成し,これら静的な値を使って型整合性をチェックしたのち,宣言で定義さ れる識別子を,対応する静的な値に束縛する. さらに,valDeclなど実行を伴うプログラムに対して,実行コードを生成する. 生成された実行コードは,宣言に含まれる構成要素に対する動的な値を 生成し,定義される識別子をそれら動的な値に束縛する.

動的な値には以下の種類がある.

組み込みデータ

18章で定義する組み込み型の実行時表現である. 原子型データ,リスト,配列,ベクタを含む. 例えば,int32は,計算機アーキテクチャが定める32ビット符号付き整数データ である.

関数クロージャ

関数型の実行時表現である.

データ構成子

データ構造を構成する組み込み関数である.

例外構成子

例外表現を構成する組み込み関数である.

レコード

レコードやタプル型の実行時表現である.

データ型表現

データ構成子によって生成されるユーザ定義のdatatype型のデータである.

例外データ

例外構成子で生成される例外を表す実行時表現である.

各宣言が生成する静的な値と動的な値のサマリを以下に示す.

宣言クラス 生成される静的値 生成される動的値 束縛対象
infix 宣言 演算子属性 変数,コンストラクタ名
val 宣言 型に応じた動的な値 変数
val rec宣言 関数クロージャ 変数
関数宣言 関数クロージャ 変数
データ型宣言 型構成子 型構成子名
コンストラクタ属性 データ構成子 データ構成子名
型の別名宣言 型関数 型構成子名
例外宣言 コンストラクタ属性 例外構成子 例外構成子名
局所宣言 内容に依存 内容に依存 内容に依存

コンパイラは,静的な値の束縛の集合を表現する静的な型環境Γを 状態として持ち,実行コードは,動的な値の束縛を表現する環境Eを状態とし て持つ. 宣言の評価はこの環境の下で行われる. 式などに含まれる名前は,その名前が現在の環境の中で束縛されている 値に評価される. コンパイラによる宣言の評価は,コンパイラの現在の型環境を宣言が生 成する静的な束縛で拡張する効果をもつ. 宣言の動的な評価,すなわちコンパラが宣言に対して生成したコードの実行は, 現在の動的な環境を,宣言が生成する動的な束縛で拡張する効果をもつ.

実行結果の表示等は,各宣言に対する動的な値の生成に伴う副作用によっ て行われる. さらに,対話型モードでは,静的な値と動的な値を表示する特殊なコー ドがコンパイラによって付加され,ユーザに表示される.

各宣言の概要と,対話型モードでの評価の例を示す. それぞれの宣言の構文の詳細定義は第23章で与える.

infixDecl

指定された識別子id(のリスト)に演算子属性(インフィッ クス属性)を与える宣言である. この宣言のスコープでは,idは,中置演算子として使用 される.

# infix 7 *;
# infix 8 ^;
# fun x ^ y = if y = 0 then 1 else x * x ^ (y - 1);
val ^ = fn : int * int -> int
# val a = 4 * 3 ^ 2
val a = 36 : int

*^がそれぞれ結合力78の演算子属性を持つので, 4 * 3 ^ 2は,*(4, ^ (3,2))と展開される. この宣言で動的な値は生成されない.

valDecl

val pat = expの形の宣言である. 式expを評価えられた値(型および動的な値)とパターン patとのパターンマッチングを行い,マッチした値を識別子に束縛す る.

# val x = 1;
val x = 1 : int

この例では,変数xに,式1を評価した結果の型 intと値1が束縛される. 変数以外にも種々の構造を表すパターンを記述できる.

# val (x, y) = (1, 2);
val x = 1 : int
val y = 2 : int

expの詳細は第19章で, パターンの詳細は第20章で定義する .

valRecDecl

一般に相互再帰的な関数定義に限定したval宣言である.

# val rec even = fn x => if x = 0 then true else odd (x - 1)
> and odd = fn x => if x = 1 then true else odd (x - 1);
val even = fn : int -> bool
val odd = fn : int -> bool

この宣言により,各識別子が対応する関数型および関数の値に束縛される.

funDecl

一般に相互再帰的な関数定義宣言である.

# fun even x = if x = 0 then true else odd (x - 1)
> and odd x = if x = 1 then true else odd (x - 1);
val even = fn : int -> bool
val odd = fn : int -> bool

この宣言により,各識別子が対応する関数型および関数の値に束縛される.

datatypeDecl

相互再帰的な新しい型構成子を定義する.

# datatype foo = A of int | B of bar and bar = C of bool | D of foo;
datatype bar = C of bool | D of foo
datatype foo = A of int | B of bar
# D (A 3);
val it = D (A 3) : bar

この宣言が評価されると,新しい型(パラメタを持たない型構成子) foo及びbarが作られ,識別子fooおよびbarがそれ ぞれ新しい型構成子に束縛される. さらに,識別子A, BおよびC, D にはデータ構成子属性が与えられ,れぞれfooおよびbar型の値を 作るデータ構成子に束縛される.

本文書では,特に混乱を引き起こさない限り上記の説明のように,型構 成子やデータ構成子を,それらが束縛される名前と同一視する.

typeDecl

識別子を型関数に束縛する宣言である.

# type ’a foo = ’a * ’a;
type ’a foo = ’a * ’a
# fun f (x:int foo) = x;
val f = fn : int * int -> int * int

この宣言により, ’aで表される型を受け取って’a * ’aを返す型関数 が生成され,識別子fooに束縛される. これ以降τ fooは,τ * τの別名として使用される.

exceptionDecl

例外構成子の宣言である.

# exception Foo of int;
exception Foo of int

この宣言が評価されると,型intを引数とする新しい例外構成子 が作られる. この宣言のスコープでは,識別子Fooにコンストラクタ属性が与 えられ,例外構成子に束縛される.

localDecl

localおよびinで囲われた宣言はendまででのみ 有効な局所的な宣言である.

# local
>   val x = 2
> in
>   val y = x + x
> end;
val y = 4 : int

宣言val x = 2のスコープはendまでである. 従ってxはこの局所宣言の外では可視ではなく, 対話セッションでもyの値のみが表示される.

16.1.2 モジュール言語の宣言の評価

モジュール言語の構成要素であるストラクチャ宣言は,宣言のリストをひ とまとまりにして名前をつける機構である. ストラクチャ式が評価されると,名前の静的な束縛の集合である型環境と 動的な束縛の集合である実行時環境が生成される. ストラクチャ宣言は,これら環境で束縛されるそれぞれの名前をストラ クチャ名でプリフィックスして得られる型環境と実行時環境を,現在の型環境と 実行時環境に追加する効果を持つ.

例えば,ストラクチャ式

struct
  val version = "4.0.0"
end

は,型環境 {𝚟𝚎𝚛𝚜𝚒𝚘𝚗:𝚜𝚝𝚛𝚒𝚗𝚐} と実行時環境 {𝚟𝚎𝚛𝚜𝚒𝚘𝚗:"4.0.0"} を生成するので,ストラクチャ宣言

structure Version =
  struct
    val version = "4.0.0"
  end

は,現在の型環境と実行時環境にそれぞれ,ロング名束縛 {Version.version:𝚜𝚝𝚛𝚒𝚗𝚐}{Version.version:"4.0.0"} を追加する効果を持つ.

ストラクチャ式にはシグネチャ制約を付加することができる. シグネチャ制約は,ストラクチャが生成する型環境の静的制約である. 変数の値の型の制約以外に,型宣言の制約なども指定できる. シグネチャ制約を持つストラクチャは,シグネチャ制約に指定された名前のみを もつ環境が生成される.

ファンクタは,ストラクチャを受け取りストラクチャを返す関数である. ただし,核言語の関数と異なり,ファンクタ定義はトップレベルに制約され,かつ, ファンクタを受け取るファンクタ等は定義できない.

各宣言の対話型モードでの評価の例を示す. それぞれの宣言の構文の詳細定義は第 24章で与える.

strDecl

ストラクチャを定義する宣言である.

# structure Version =
>   struct
>     val version = "4.0.0"
>   end;
structure Version =
struct
  val version = "4.0.0" : string
end

この宣言により,識別子Versionがストラクチャを表す型環境に束縛され, ロング識別子Version.versionが,string型と動的値 "4.0.0"に束縛される.

sigDecl

識別子をシグネチャに束縛する宣言である.

# signature VERSION =
>  sig
>    val version : string
>  end;
signature VERSION =
struct
  val version : string
end

この宣言により,VERSIONがシグネチャに束縛される. シグネチャは,ストラクチャで定義される各名前が持つ型などの属性を 表現である.

functorDecl

ストラクチャからストラクチャを生成する関数であるファンクタを定義する宣言である.

functor System(V:VERSION) =
  struct
    val name = "SML#"
    val version = V.version
  end;

この宣言によって,Systemが,VERSIONシグネチャをもつストラクチャを うけとり,nameversionを要素とするストラクチャを返すファンクタに束縛される.

localTopdecl

localおよびinで囲われた宣言はendまででのみ 有効な局所的な宣言である.

# local
>   structure V = Version
> in
>   structure Release = struct
>     val date = "2021-04-06"
>     val version = V.version
>   end
> end;
val Release =
struct
  val date = "2021-04-06" : string
  val version = "4.0.0" : string
end

宣言structure V = Versionのスコープはendまでである. 従ってVはこの局所宣言の外では可視ではなく, 対話セッションでもReleaseのみが表示される.