プログラミング言語SML#解説

Chapter 16. 名前の種類とスコープ規則

SML#の構文の静的意味と動的意味を定義する前に,プログラム に使用される名前の有効範囲(スコープ規則)を定義する.

13.2で定義した通り,識別子は,以下の 7種類のクラスの名前として使用される.

識別子名 名前
vid 変数とデータコンストラクタ名
strid ストラクチャ名
sigid シグネチャ名
funid ファンクタ名
tycon 型構成子名
tyvar 型変数名
lab レコードラベル

この中で,' ...の形をした型変数名は,他の名前とは字句構造上区 別される. それ以外の名前の字句構造は重なりを持つ. 例えばAは,型変数名以外の他の全ての名前として使用される. 第LABEL:sec:syntaxで定義する通り,これら同一の識別子のクラスは, それがプログラム中に現れる位置によって一意に定まるように,文法が定義され ている. さらにこれらの名前は,そのクラス毎に独立な名前空間で管理されるた め,各クラスの名前として同一の字句を使用することができる. 以下は,名前の使用の例である.

(* 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

7行目の最初のAは1行目の変数Aを,2番目のA は2行目の型構成子Aを参照しており,6行目の最初のAは5行目 のストラクチャを参照している.

レコードラベル以外の名前は,プログラムの構文によって定義され,そ の定義が有効な範囲で参照される. この定義の有効範囲をスコープと呼ぶ. SML#は,Algolの伝統を受け継ぐブロック構造を持つ言語であり, 名前の定義と参照の関係は,コンパイル時に静的に決定される. さらに,このスコープは,「新しい定義は以前の定義を隠す」ことを原 則とする静的スコープ規則に従う.

名前の定義を含む構文とそれによって定義される名前は以下の通りであ る. 定義される名前の詳細は,各構文の定義のところで詳述する.

  1. 分割コンパイルのインタフェイスファイル. インタフェイスファイルに含まれる宣言仕様の各名前が定義される. 例えば,インタフェイスファイルに

    _require "myLibrary.smi"
    val x : int
    datatype foo = A of int | B of bool

    の記述があれば,変数x,データ構成子A,B,型名foo が定義される.

  2. ストラクチャ宣言. ストラクチャ名が定義される. 例えばstructure S = によってストラクチャ名 Sが定義される.

  3. ファンクタ宣言. ファンクタ名とファンクタの引数の宣言仕様の各名前が定義される. 例えばfunctor F(type foo) = によってファンクタ名 Fと型構成子名fooが定義される.

  4. 型構成子の別名宣言. 型構成子名と型構成子の型変数引数が定義される. 例えばtype foo = によって型名(引数を持たない型構 成子)fooが定義される.

  5. データ型宣言. 型構成子名と型構成子の型変数引数,データ構成子名が定義される. 例えばdatatype 'a foo = A of int | B of boolによって 型名foo,型変数'a,データ構成子名ABが定義される.

  6. 例外宣言. 例外構成子名が定義される. 例えばexception Eによって例外名Eが定義される.

  7. 値束縛宣言. 束縛パターンに含まれる変数が定義される. 例えば,xがデータ構成子として定義されていない位置で val x = 1と宣言すると変数xが定義される.

  8. 関数宣言. 関数名と関数の引数パターンに含まれる変数が定義される. 例えば,xがデータ構成子として定義されていない位置で fun f x = 1と宣言すると変数fが定義される.

  9. fn. 引数パターンに含まれる変数が定義される. 例えば,xがデータ構成子として定義されていない位置で fn x => xでは,変数xが定義され使用されている.

  10. case. ケースパターンに含まれる変数が定義される. 例えば,xがデータ構成子として定義されていない位置で case y of A x => xでは変数xが定義され使用されている.

  11. SQL式_sql db => expの形のSQL式には,変数定義が含まれるもの がある. これらは第LABEL:sec:sqlExp節で定義する.

これら名前の定義は,それぞれの名前空間毎に,その名前定義が現れる 構文によって決まるスコープ(変数が参照できる範囲)を持つ. ALGOL系ブロック構造言語の伝統を受け継ぐSML#言語では, スコープを区切る構文は入れ子構造を成すため,名前定義のスコープも一般に 入れ子状に重なりをもつ. 各名前定義のスコープは,内側に現れる同一種類の同名の変数のスコー プを除いた部分と定義される.

SML#言語のスコープを区切る構文とそれら構文に現れる名前の 定義のスコープ規則を定義する. 実際のスコープは,以下定義されるスコープから,内側に現れる同種類 の同名の名前のスコープを除いた部分である.

  • 分割コンパイル単位.

    分割コンパイル単位は一つのソースファイル srcFule.smlである.

    srcFile.smlのトップレベルの宣言に含まれる名前定 義のスコープは,それに続くファイル全体である.

    さらに,このソースファイルに対応するインターフェイスファイル srcFile.smiが定義する名前のスコープは,ソースファイル全 体である. インターフェイスファイルsrcFile.smiで定義される 名前は,インターフェイスファイルに直接含まれる各_require文で参照 されるインターフェイスファイルが定義するすべての名前である.

  • ストラクチャ生成文.

    struct endのトップレベルの宣言に含まれる名前定 義のスコープは,その定義に続くendまでの部分である.

  • local構文.

    構文local decl1 in decl2 enddecl1の中の宣言に含まれる名前のスコープは, その宣言以降のdecl1の部分およびdecl2全体である. decl2の中の宣言に含まれる名前のスコープは, その宣言以降のdecl2の部分である.

  • let構文.

    構文let decl1 in exp enddecl1の中に現れる宣言に含まれる名前定義のスコープは その宣言以降のdecl1の部分およびexpである.

  • fun宣言

    構文

    fun id pat1,1  pat1,n = exp1
         
      |
    id patm,1  patm,n = expm

    において,関数名idのスコープは,exp1から expmの各式, パターンpati,jに含まれる変数定義のスコープは,対応する expiである.

  • fn

    構文

    fn pat1 => exp1  |    | patn => expm

    において,パターンpatiに含まれる変数定義のスコープは,対応するexpiである.

  • case

    構文

    case exp of pat1 => exp1  |    | patn => expm

    において,パターンpatiに含まれる変数定義のスコープは,対応するexpiである.