Chapter 7 言語拡張

このページは最後に更新されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

last mod. 2008-09-21 (日) 23:33:39

この章では Objective Caml で実装されているがリファレンスマニュアルで説明されていない言語拡張や便利な機能を説明します。

int32, int64 と nativeint 型の 整数リテラル (Integer literals for types int32, int64 and nativeint)

                       int32-literal ::=  integer-literal l 
                       int64-literal ::=  integer-literal L 
                   nativeint-literal ::=  integer-literal n 

int型の代わりに、 整数リテラルの後に l, L または n を置く事で、その整数がそれぞれ型 int32, int64 または nativeint 型を持つことを指示できます。ライブラリモジュールInt32, Int64NativeInt は これらの整数型の上の操作を提供します。

ストリームとストリームパーザ (Streams and stream parsers)

ストリームとストリームパーザの構文はもう Objective Caml に組み込まれていませんが、CamlP4 文法拡張を通して利用可能です。詳細は CamlP4 のリファレンスマニュアルを見てください。 ストリームの上の基本的な操作は 標準ライブラリの Stream[] モジュールで利用可能です。 ストリームとストリームパーザを使うプログラムをコンパイルする場合は、ocamlc や ocamlopt に -pp camlp4o オプションを指定してください。対話式システムで使う場合は、ocaml を起動した後 #load "camlp4o.cma";; としてください。

値の再帰的定義 (Recursive definitions of values)

6.7.1節で言及したように、let rec 束縛構文は 再帰関数と同様に、 関数以外のクラスの再帰的定義もサポートします。 例えば:

       let rec name_1 = 1 ::  name_2 and  name_2 = 2 ::  name_1 in  expr 

これは name_1 を循環的なリスト 1::2::1::1::... に、 name_2 を循環的なリスト 2::1::2::1::... に束縛します。 非形式的には、 可能な定義のクラスは 定義されている名前が関数本体の内部かデータ構築子(FIXME:data constructor)の引数としてのみ出現するような定義から構成されます。

より精確には、 次の式を考えてみてください:

        let rec name_1 =  expr_1 and ... and  name_n =  expr_n in  expr 

これは 各 expr_1 ... expr_n が name_1 ... name_n に関して静的に構築でき 直接 name_1 ... name_n に直接リンクされていない場合に許可されます。

「式 e が name_1 ... name_n に関して静的に構築できる」とは、 少なくとも次のいずれかの条件のうちひとつが真となる場合です:

  • e に どの name_1 ... name_n も自由に出現していない
  • e は変数である
  • e は fun ... -> ... の形である
  • e は function ... -> ... の形である
  • e は lazy ( ... ) の形である
  • e は 次のうちどれか一つの形である ここで expr_1 ... expr_m はそれぞれ name_1 ... name_n に関して静的に構築でき、 expr_0 は name_1 ... name_n に関して静的に構築でき、 xname_1 ... xname_m は次の形である:
    • let [rec] xname_1 = expr_1 and ... and xname_m = expr_m in expr_0
    • let module ... in expr_1
    • constr ( expr_1, ... , expr_m)
    • `tag-name ( expr_1, ... , expr_m)
    • [| expr_1; ... ; expr_m |]
    • { field_1 = expr_1; ... ; field_m = expr_m }
    • { expr_1 with field_2 = expr_2; ... ; field_m = expr_m } ここで expr_1 は name_1 ... name_n に直接リンクされていないこと
    • ( expr_1, ... , expr_m )
    • expr_1; ... ; expr_m

式 e は 次の場合に 変数 に直接リンクされていると言います:

  • e は 名前である
  • e は expr_1; ... ; expr_m の形で、 expr_m が 名前に直接リンクされている
  • e は let [rec] xname_1 = expr_1 and ... and xname_m = expr_m in expr_0 の形である ここで expr_0 は xname_i (expr_i が名前に直接リンクされている) のどれかに直接リンクされているとき name

レンジ パターン (Range patterns)

Objective Caml ではパターンに 'c' .. 'd' という (2 つの文字を .. でつないでいる) 形を、 'c' | 'c1' | 'c2' | ... | 'cn' | 'd' というパターンの省略形として指定することが出来ます。c1, c2, ..., cn は ASCII 文字列で c と d の間に出てくる文字です。たとえば、パターン '0'..'9' はすべての数字にマッチします。

アサーション チェック (Assertion checking)

Objective Caml ではデバッグのために assert を入れることが出来ます。assert expr という式は expr が true の時 () を返し、false の時例外 Assert_failure を発生します。Assert_failure には引数としてソースファイル名と expr の位置が指定されています。Assertion チェックを行わない場合は、コンパイラオプションに -noassert を指定してください。

特別な場合として、assert false は型多相な raise (Assert_failure ...) に還元されます (-noassert オプションを指定しても Assertion チェックが行われます) 。

遅延評価 (Lazy evaluation)

lazy expr という式は、expr の評価をカプセル化した Lazy.t 型の値 v を返します。引数の expr はプログラムのこの時点では評価されていません。Lazy.force に v を適用することで初めて評価され、expr の実際の値が得られます。次に Lazy.force に v を適用しても評価は行われません。詳細については、標準ライブラリの Lazy モジュールを見てください (Module Lazy) 。

ローカル モジュール (Local modules)

let module module-name = module-expr in expr という式はローカルモジュールを表します。expr の中でだけ、module-name という識別子がモジュール module-expr に割り当てられます。expr を評価した値を返します。たとえば以下のように使います。

        let remove_duplicates comparison_fun string_list =
          let module StringSet =
            Set.Make(struct type t = string
                            let compare = comparison_fun end) in
          StringSet.elements
            (List.fold_right StringSet.add string_list StringSet.empty)

プライベート型 (Private types)

     type-representation ::=  ...                                        
                          |   = private constr-decl  { | constr-decl }   
                          |   = private { field-decl  { ; field-decl } } 

プライベート型は バリアント型か レコード型です。 これらの値は パターンマッチ や レコードアクセスのための expr . field 構文 によって分解できます。 しかしながら、 これらの型の値は 構築子(FIXME:constrcutor)の適用や レコード構築では 構築できません。 さらに、 プライベートなレコード型のミュータブル (FIXME:mutable) なフィールドへの代入は 許可されません。

プライベート型の典型的な 使用法は、モジュールのエクスポートシグネチャ (FIXME) において、 プライベート型の値が モジュールで提供される関数を経由することを保証する一方、 定義しているのモジュールの外で パターンマッチングを許可する場合です。 例えば:

        module M : sig
                     type t = private A | B of int
                     val a : t
                     val b : int -> t
                   end
                 = struct
                     type t = A | B of int
                     let a = A
                     let b n = assert (n > 0); B n
                   end

ここで、 プライベート宣言は 型 M.t のどんな値も コンストラクタ B の引数が 正の整数になることを保証します。

パラメータの variance (FIXME:可変性?) については、 プライベート型は抽象型(FIXME) と同様に 扱われます。 つまり、 もしプライベート型がパラメータを持つ場合、その variance は プレフィクス '+' か '-' を付けることで 陽に 与えるか、さもなくば invariant (FIXME:不変とか) になります。 With respect to the variance of their parameters, private types are handled like abstract types. That is, if a private type has parameters, their variance is the one explicitly given by prefixing the parameter by a `+' or a `-', it is invariant otherwise.

再帰的モジュール (Recursive modules)

    definition ::=  ...                                                       
                 |   module rec module-name :  module-type =  module-expr   {
               and module-name:  module-type =  module-expr } 
 specification ::=  ...                                                       
                |   module rec module-name :  module-type  { and module-name: 
                module-type }                                

再帰的モジュール定義は、 'module rec' ... 'and' ... 構文で導入できますが, これは通常のモジュール定義 module module-name = module-expr と モジュール仕様 module module-name : module-type を、module-expr や module-type で定義途中のモジュール識別子を参照できるように一般化したものです。 典型的な再帰的モジュール定義は:

    module rec A : sig
                     type t = Leaf of string | Node of ASet.t
                     val compare: t -> t -> int
                   end
                 = struct
                     type t = Leaf of string | Node of ASet.t
                     let compare t1 t2 =
                       match (t1, t2) with
                         (Leaf s1, Leaf s2) -> Pervasives.compare s1 s2
                       | (Leaf _, Node _) -> 1
                       | (Node _, Leaf _) -> -1
                       | (Node n1, Node n2) -> ASet.compare n1 n2
                   end
        and ASet : Set.S with type elt = A.t
                 = Set.Make(A)

これには 次の仕様を与えることができます:

    module rec A : sig
                     type t = Leaf of string | Node of ASet.t
                     val compare: t -> t -> int
                   end
        and ASet : Set.S with type elt = A.t

これは Objective Caml の実験的な拡張です: 許可される再帰的定義のクラスは、その動的な意味論と同様に、最終的なものではなく将来のリリースで変更されるかもしれません。

今のところ、 コンパイラは 再帰的に定義されたモジュール識別子のあいだのすべての依存関係のサイクルが最低1つの「安全な」モジュールを通ることを要求します。 モジュールが安全であるとは、 モジュールが含んでいるすべての値定義が関数型 typexpr_1 -> typexpr_2 をもつことです。 再帰的モジュールの評価は 安全なモジュールの初期値を構築することで進行し, 全ての (関数)値 を fun _ -> raise Undefined_recursive_module に束縛します. その後にモジュール式全体が評価され,安全なモジュールの初期値がここで計算された値で置き換えられます. もしある安全なモジュールに含まれる関数がこの計算の途中で適用された場合 (これは ill-founded (FIXME:日本語訳) な再帰的定義に対応します)、 Undefined_recursive_module 例外が raise されます。

プライベート 列型 (Private row types)

(FIXME:節タイトル↑の訳)

                     type-equation ::=  ...               
                                    |   = private typexpr 

プライベート列型 (FIXME) は型の構造の一部分が抽象的になるような型の略記です。 具体的には、 上の typexpr はオブジェクト型か 多相バリアント型で、refinement (FIXME:訳) の余地がなければなりません。 もしプライベート宣言がインタフェースで用いられた場合、対応する実装は グラウンドなインスタンスか、refineされたプライベート型を提供します。

   module M : sig type c = private < x : int; .. > val o : c end =
     struct
       class c = object method x = 3 method y = 2 end
       let o = new c
     end

この宣言は y メソッドを隠し、 さらに他の閉じたオブジェクト型と型 c を非互換にします。これは o だけが c 型をもつことを意味します。 この側面において これはプライベートなレコード型と同様に振る舞います。 しかしプライベート列型はインクリメンタルな refinement (FIXME) についてより柔軟です。 この機能は ファンクタとの組み合わせでも使えます。

   module F(X : sig type c = private < x : int; .. > end) =
     struct
       let get_x (o : X.c) = o#x
     end
   module G(X : sig type c = private < x : int; y : int; .. > end) =
     struct
       include F(X)
       let get_y (o : X.c) = o#y
     end

多相バリアントは 新しい構築子(FIXME:constructor)を追加するか、宣言された構築子の disparition (FIXME:訳) を認めるかの 2通りの方法で refine できます。 2番目のケースは プライベートバリアント型 (プライベート型の値を構築できない) に対応し、 一方1番目のケースは パターンマッチにおいて追加のコンストラクタを扱うためのデフォルトケース (FIXME:default case) を要求します。 (訳注:すべてモジュールの外部での話です。もちろんモジュール内部では private は無視できます)

   type t = [ `A of int | `B of bool ]
   type u = private [< t > `A ]
   type v = private [> t ]

型 u では、(`A n) の形で値を作ることができますが (`B b) はできません。 型 v では、 値の構築は制限されませんが、パターンマッチはデフォルトケースを持つ必要があります。

抽象型 (FIXME:abstract type) や プライベート型 と似て、 型パラメータの variance (FIXME:訳) は推論されず、陽に与えなければなりません。

新規 編集 添付