6.7 式

6.7.1. 基本式
6.7.2. 制御構文
6.7.3. データ構造上の演算
6.7.4. 演算子
6.7.5. オブジェクト
6.7.6. 型変換
expr ::= value-path
| constant
| ( expr )
| begin expr end
| ( expr : typexpr )
| expr , expr { , expr }
| constr expr
| ` tag-name expr
| expr :: expr
| [ expr { ; expr } ]
| [| expr { ; expr } |]
| { field = expr { ; field = expr } }
| { expr with field = expr { ; field = expr } }
| expr { argument }+
| prefix-symbol expr
| expr infix-op expr
| expr . field
| expr . field <- expr
| expr .( expr )
| expr .( expr ) <- expr
| expr .[ expr ]
| expr .[ expr ] <- expr
| if expr then expr [ else expr ]
| while expr do expr done
| for ident = expr ( to | downto ) expr do expr done
| expr ; expr
| match expr with pattern-matching
| function pattern-matching
| fun multiple-matching
| try expr with pattern-matching
| let [ rec ] let-binding { and let-binding } in expr
| new class-path
| object class-body end
| expr # method-name
| inst-var-name
| inst-var-name <- expr
| ( expr :> typexpr )
| ( expr : typexpr :> typexpr )
| {< inst-var-name = expr { ; inst-var-name = expr } >}
| assert expr
| lazy expr
argument ::= expr
| ~ label-name
| ~ label-name : expr
| ? label-name
| ? label-name : expr
pattern-matching ::= [ | ] pattern [ when expr ] -> expr { [ | ] pattern [ when expr ] -> expr }
multiple-matching ::= { parameter }+ [ when expr ] -> expr
let-binding ::= pattern = expr
| value-name { parameter } [ : typexpr ] = expr
parameter ::= pattern
| ~ label-name
| ~ ( label-name [ : typexpr ] )
| ~ label-name : pattern
| ? label-name
| ? ( label-name [ : typexpr ] [ = expr ] )
| ? label-name : pattern
| ? label-name : ( pattern [ : typexpr ] [ = expr ] )

下の表は演算子と閉じていない構文の相対的な優先順位と結合性の表で、それぞれ優先順位の高い構文から順に並んでいます。 前置演算子と中置演算子の部分の「*...」は「* で始まるシンボル」を表します。

構文または演算子結合性
前置演算子-
. .( .[ -
関数適用、構成子適用、 assertlazy
- -. (前置)-
**… lsl lsr asr
*… /… %… mod land lor lxor
+… -…
::
@… ^…
=… <… >… |… &… $…
& &&
or ||
,-
<- :=
if-
;
let match fun function try-

6.7.1 基本式

定数

constant に含まれる式はその定数に評価されます。

値パス

value-path に含まれる式は現在の評価環境の中でこのパスに束縛された値に評価されます。 パスは値の名前かモジュールの値要素へのアクセスパスです。

括弧つきの式

( expr ) と式 begin expr endexpr としては同じ値です。 どちらの構文も意味上は等価ですが、制御構造の中では

if … then begin … ; … end else begin … ; … end
      

と、 begin ... end を使い、 それ以外で式をグループ化したいときには ( ... ) を使うのがよいスタイルです。

括弧付きの式には ( expr : typexpr ) のように型制約をつけることができます。 これにより、 exprtypexpr に適合するように制約をつけられます。

括弧付きの式には ( expr [ : typexpr ] :> typexpr ) のように型変換をつけられます( 6.7.6 節「型変換 」 を参照してください)。

関数適用

関数適用は(ラベルつきの)式を並べることで表されます。 式 expr argument1 ... argumentn は式 exprargument1 から argumentn 中の式を評価します。 式 expr は関数値 f に評価されなければなりません。 その値が argument1 から argumentn の値に適用されます。

exprargument1argumentn の評価順序は規定されていません。

実引数と仮引数はそれぞれのラベルについて対応していなければなりません。 同一のラベルのついた引数同士や、ラベルのない引数同士以外では引数の順序に意味はありません。

expr の型で仮引数が省略可能だとされている場合(ラベルの前に ? がついている場合)、引数自体にも ? が前置されていなければ、対応する仮引数は自動的に Some 構成子でラップされます。 引数自体に ? が前置されている場合には、引数がそのまま渡されます。 ラベルなしの引数が渡されたとき、それに対応する仮引き数の前にひとつ以上の省略可能な仮引数があると、これらの省略可能引数にはデフォルト値、すなわち None が渡されます。 その他の指定されなかった(対応する引数のなかった)仮引数は、省略可能なものもそうでないものも、そのまま保持され、関数の戻り値は指定されなかった仮引数から f の本体への関数になります。

特別な場合として、関数の仮引数の個数がわかっていて、かつ実引数にはすべてラベルがついていず、さらに実引数の個数が省略不可能な仮引数の個数と一致していた場合には、ラベルを無視し、実引数を省略不可能な引数に定義時の順番で対応づけます。 このとき省略可能引数にはデフォルト値が渡されます。

引数の順序とラベルが完全に一致する場合を除き、省略可能引数が存在しない場合は、常に関数の型は適用の時点で既知でなければなりません。 型制約を追加すればこれを保証することができます。 導出される型が一意であることは -principal モードで検査することができます。

関数定義

関数定義のための構文形式はふたつあります。 ひとつ目は予約語 function で始まるものです。


function pattern1 -> expr1
  | ...
  | patternn -> exprn
      

この式は一引数の関数値に評価されます。 この関数が値 v に適用されると、その値を pattern1 から patternn までのパターンとパターンマッチします。 これらのうちひとつが成功した場合、つまり、値 v が何らかの i について patterni とマッチしたとき、選択されたパターンと対応づけられた式 expri が評価され、その値が関数適用の値になります。 expri はパターンマッチにより生成された束縛を加えた環境で評価されます。

引数 v に対して複数のパターンが一致する場合、関数定義の中で最初に現れたものが選択されます。 引数に一致するパターンがない場合には、 Match_failure 例外が発生します。

もうひとつの形式は予約語 fun を使うものです。

fun parameter1 ... parametern -> expr

この式は次のものと等価です。

fun parameter1 -> ... fun parametern -> expr

パラメータパターン ~var~(var [: typexpr ]) はそれぞれ ~var:var~var:(var [ : typexpr ]) の略記法です。省略可能引数にも同様の略記法があります。

fun ? label ( pattern = expr0 ) -> expr の形式の関数は次のものと等価です。

fun ? label ident -> let pattern = match ident with Some ident -> ident | None -> expr0 in expr

ここで ident は新しい変数です。

このような変換を行なうと、式は次のような形式になります。

fun [ label1 ] pattern1 -> ... fun [ labeln ] patternn -> expr

ラベルを無視すればこれは次と等価です(ラベルは関数適用時にだけ意味を持ちます)。

function pattern1 -> ... function patternn -> expr

すなわち、上の fun 式は n 引数のカリー化関数に評価されます。 この関数に値 v1 ... vmn 回適用すると、 v1 ... vm はパターン pattern1 ... patternn と並行にパターンマッチされます。 パターンマッチが成功した場合、 expr をパターンマッチによって生成された束縛を加えた環境で評価します。 パターンマッチに失敗した場合には Match_failure 例外が発生します。

パターンマッチ中のガード

functionfunmatchtry 構文中の)パターンマッチの各分岐にはガード式をつけることができます。ガード式は真理値を返す任意の式で、その分岐が選択される場合には true に評価されます。 ガードは -> の前に書き、予約語 when で始まります。

function parameter1 [ when cond1 ] expr1
  | ...
  | parametern [ when condn ] exprn
      

パターンマッチは先に述べたように行なわれますが、値がガード condi を持つパターン patterni にマッチした場合、式 condi が(パターンマッチにより生成された束縛を加えた環境で)評価されます。 conditrue に評価された場合には、通常と同じように expri が評価され、その値がパターンマッチの値として返されます。 condifalse に評価された場合には、 patterni に続くパターンのパターンマッチを再開します。

局所定義

let 構文と let rec は名前を局所的に束縛します。

let pattern1 = expr1 and ... and patternn = exprn in expr

は、 expr1 ... exprn が何らかの順序で評価されは、その値を pattern1 ... patternn とマッチさせます。 パターンマッチに成功した場合には、パターンマッチにより生成された束縛を加えた環境で expr が評価され、 expr の値が let 式全体の値として返されます。 パターンマッチのうちひとつでも失敗したものがあれば、 Match_failure 例外が発生します。

変数を関数値に束縛するための特別の構文もあります。

let ident = fun parameter1 ... parameterm -> expr

と書く代わりに次のように書きます。

let ident parameter1 ... parameterm = expr

let rec を使うと名前を再帰的に定義することができます。

let rec pattern1 = expr1 and ... and patternn = exprn in expr

上で述べた let 構文との違いは、パターンマッチによる名前の束縛が式 expr1 から exprn が評価されるときに、既に確立されていると考えるということだけです。 つまり、 expr1 から exprn では pattern1 から patternn で束縛された識別子を参照することができ、それらが let rec 構文の本体の expr 内と同じ値であると期待することができます。

下に示すように、 expr1 から exprn が関数定義(fun ... または function ...)であり、 pattern1 から patternn が単なる変数名であれば、再帰定義は上で述べたように振る舞うことが保証されます。

let rec name1 = fun ... and ... and namen in expr

ここでは、 name1 から namenexpr に局所的な相互再帰関数として定義しています。

他の形式 let rec 定義の振舞いは実装系依存です。 7.3 節「値の再帰的定義」 で述べるように、現在の実装では特定のクラスの値の再帰定義もサポートしています。

6.7.2 制御構文

逐次実行

expr1 ; expr2 はまず expr1 を評価し、次に expr2 を評価し、 expr2 の値を返します。

条件分岐

if expr1 then expr2 else expr3expr1 が真理値の true に評価された場合には expr2 の値に評価され、 false に評価された場合には expr3 の値に評価されます。

else expr3 の部分は省略することができ、その場合は else () になります。

マッチ式

match expr 
 with pattern1 -> expr1 
    | ... 
    | patternn -> exprn
      

expr の値を pattern1 から patternn にマッチさせます。 patterni へのパターンマッチが成功した場合、対応する式 expri が評価され、その値が match 式全体の値になります。 expri はパターンマッチによって生成された束縛を加えた環境で評価されます。 expr の値に対して複数のパターンがマッチする場合、 match 式に最初に現れたものが選択されます。 マッチするものがなかった場合には Match_failure 例外が発生します。

論理演算子

expr1 && expr2expr1expr2 の両方が true に評価されたとき true に評価され、そうでなければ false に評価されます。 最初の要素 expr1 が先に評価されます。 次の要素 expr2 は最初の要素が false に評価された場合には評価されません。 したがって、 expr1 && expr2 振舞いは次の式とまったく同じです。

if expr1 then expr2 else false

expr1 || expr2expr1expr2 のいずれかが true に評価されたとき true に評価され、そうでなければ false に評価されます。 最初の要素 expr1 が先に評価されます。 次の要素 expr2 は最初の要素が true に評価された場合には評価されません。 したがって、 expr1 || expr2 振舞いは次の式とまったく同じです。

if expr1 then true else expr2

論理演算子 &&& と同義で、 or|| と同義です。

ループ

while expr1 do expr2 doneexpr1true に評価される間、繰り返し expr2 を評価します。 ループ条件 expr1 は各繰り返しの最初に評価され検査されます。 while ... done 式全体はユニット値 () に評価されます。

for name = expr1 to expr2 do expr3 done は、まず式 expr1expr2 (境界)を整数値 np に評価します。 次にループの本体 expr3が、 name が次々と nn + 1、p - 1、 p に束縛された環境で繰り返し評価されます。 n > p であればループ本体は一度も評価されません。

for name = expr1 downto expr2 do expr3 done も同じように評価されますが、 namenn - 1、 ...、 p + 1、 p に束縛されて行きます。 n > p であればループ本体は一度も評価されません。

どちらの場合も、 for 式全体はユニット値 () に評価されます。

例外処理

try expr 
 with pattern1 -> expr1 
    | ...
    | patternn -> exprn
      

は式 expr を評価し、例外が発生しなければその値を返します。 例外が発生した場合、例外の値が pattern1 から patternn とパターンマッチされます。 patterni とマッチした場合には、その対応する expri が評価され、その値が try 式全体の値になります。 expri の評価は、パターンマッチにより拡張された環境で行なわれます。 複数のパターンがマッチする場合、最初のものが選択されます。 マッチするものがない場合、その例外値を再送出します。 つまり、 try 構文は透過的に「素通り」されるのです。

6.7.3 データ構造上の演算

直積

expr1, ..., exprn は、式 expr1 から exprnn 個組に評価されます。 部分式の評価順序は規定されていません。

バリアント

constr expr は、構成子が constr で、引数が expr の値となるバリアントに評価されます。

リストには一種の糖衣構文が用意されています。 expr1 :: expr2 は、構成子 ( :: ) を引数 (expr1, expr2) に適用したものを意味し、頭部が expr1、尾部が expr2 であるリストに評価されます。 [expr1; ...; exprn]expr1 :: ... :: exprn :: [] と等価で、 expr1 から exprn を要素とするリストに評価されます。

多相バリアント

`tag-name expr はタグが tag-name であり、引数が expr の値であるような多相バリアント値に評価されます。

レコード

{ field1 = expr1; ...; fieldn = exprn } はレコード値 { field1 = v1; ...; fieldn = vn } に評価されます。 ここで、 vii = 1, ..., n について expri の値です。 field1 から fieldn はすべて同一のレコード型に属していなければなりません。 レコード式にはこのレコード型に属するフィールドすべてが丁度一回ずつ現れなければなりませんが、順番はどのようであってもかまいません。 expr1 から exprn の評価順序は規定されていません。

{ expr with field1 = expr1; ...; fieldn = exprn }field1 から fieldnexpr1 から exprn に等しく、それ以外のフィールドはレコード expr と同じ値を持つ、新たなレコードを構成します。 別の言い方をすれば、 field1 から fieldnexpr1 から exprn に初期化し、それ以外のフィールドは expr を浅くコピーしたレコードを返します。

expr1.fieldexpr1 がレコード値に評価され、その field に対応づけられた値が返ります。

expr1.field <- expr2expr1 がレコード値に評価され、そのレコードの field に対応づけられた値をその場で expr2 の値に修正します。 この操作は、このレコード型の定義で fieldmutable と宣言されていた場合にだけ許されます。 式全体はユニット値 () に評価されます。

配列

[| expr1; ...; exprn |]n 要素の配列に評価され、各要素は順に expr1 から exprn に初期化されます。 これらの式の評価順序は規定されていません。

expr1.(expr2)expr1 の表す配列の expr2 番目の要素の値を返します。 配列の要素を n としたとき、最初の要素は 0 番目で、最後の要素は n - 1 番目になります。 領域外にアクセスした場合には Invalid_argument 例外が発生します。

expr1.(expr2) <- expr3 は、 expr1 の表す配列の expr2 番目の要素を expr3 の値で置き換えます。 領域外にアクセスした場合には Invalid_argument 例外が発生します。 式全体の値は () になります。

文字列

expr1.[expr2]expr1 の表す文字列の expr2 の文字の値を返します。 文字列の長さを n としたとき、最初の文字は 0 番目で、最後の文字は n - 1 番目になります。 範囲外にアクセスした場合には Invalid_argument 例外が発生します。

expr1.[expr2] <- expr3 は、 expr1 の表す文字列の expr2 番目の文字を expr3 の値で置き換えます。 範囲外にアクセスした場合には Invalid_argument 例外が発生します。 式全体の値は () になります。

6.7.4 演算子

infix-symbol に属するシンボルは、予約語 *=or& と同じく式の間に中置することができます。 prefix-symbol に属するシンボルは式に前置することができます。

前置シンボルと中置シンボルには固定された意味はなく、単純に、そのシンボルに対応する名前に束縛された関数の適用と解釈されます。 式 prefix-symbol expr( prefix-symbol ) expr という適用と解釈されます。 同様に、 expr1 infix-symbol expr2( infix-symbol ) expr1 expr2 という適用と解釈されます。

下の表に初期環境で定義されているシンボルとその意味を示します(詳細は 19 章「コアライブラリのコアライブラリモジュール Pervasive の説明を読んでください)。 これらの意味は let ( infix-op ) name1 name2 = ... としていつでも変更することができます。

演算子 意味
+整数の足し算
-(中置)整数の引き算
-(前置)整数の符号反転
*整数の掛け算
/整数の割り算。第二引数が 0 のときは Division_by_zero 例外が発生する
mod整数の法。第二引数が 0 のときは Division_by_zero 例外が発生する
land整数のビットごとの論理積
lor整数のビットごとの論理和
lxor整数のビットごとの排他的論理和
lsl整数のビットごとの左論理シフト
lsr整数のビットごとの右論理シフト
asr整数のビットごとの右算術シフト
+.浮動小数点数の足し算
-.(中置)浮動小数点数の引き算
-.(前置)浮動小数点数の符号反転
*.浮動小数点数の掛け算
/.浮動小数点数の割り算
**浮動小数点数の累乗
@リストの連結
^文字列の連結
!参照外し(参照の現在の内容を返す)
:=参照への代入(第一引数として与えられた参照を第二引数の値で更新する)
=構造的等価性のテスト
<>構造的不等性のテスト
==物理的等価性のテスト
!=物理的不等性のテスト
<「小なり」テスト
<=「小なりイコール」テスト
>「大なり」テスト
>=「大なりイコール」テスト

6.7.5 オブジェクト

オブジェクトの生成

class-path がクラスの本体に評価されるとき、 new class-path はこのクラスのインスタンス変数とメソッドを格納したオブジェクトに評価されます。

class-path がクラス関数に評価されるときには、 new class-path はクラス関数と同一個数の引数を期待し、このクラスのオブジェクトを返す関数に評価されます。

オブジェクトの直接生成

object class-body end として直接オブジェクトを生成するのは、 class class-name = object class-body end として局所的にクラスを定義し(クラス本体class-body の構文参照)、即座にそのクラスに対して new class-name として単一のオブジェクトを生成するのと、操作の上では等価です。

直接生成されたオブジェクトの型づけには定義済みのクラスのインスタンスを生成した場合と異なる点がふたつあります。 まずひとつには、推論された型に自由な型変数が含まれることがあります。 もうひとつには、直接生成されたオブジェクトのクラス本体は拡張されることがないので、そのオブジェクト自身の型は閉じたオブジェクト型に単一化することができます。

メッセージ送信

expr#method-nameexpr の表すオブジェクトのメソッド method-name を起動します。

method-name が多相メソッドであった場合、メソッドの型は起動する箇所で既知でなければなりません。 expr が新しいオブジェクト(let ident = new class-path ...)の名前であるか、型制約がある場合にはこれは成り立ちます。 型導出の一意性は -principal モードでチェックすることができます。

インスタンス変数へのアクセスと変更

クラスのインスタンス変数は、インスタンス変数を定義したクラスと同一のクラスで定義されたメソッドやインスタンス変数を定義したクラスを継承したクラス内から参照できます。 inst-var-name は与えられたインスタンス変数の値に評価されます。 inst-var-name <- exprexpr の値をインスタンス変数 inst-var-name に代入します。 このインスタンス変数は mutable でなければなりません。 代入式全体は () に評価されます。

オブジェクトの複製

ライブラリ関数 Oo.copyOo モジュール参照)を使うとオブジェクトを複製することができます。 メソッド中で、 {< inst-var-name = expr { ; inst-var-name = expr } >} とすると、指定したインスタンス変数が対応する式の値で置き換えられた自身のコピーが返ります。 戻り値のオブジェクトのインスタンス変数うち指定されなかったものはコピー元と同じ値になります。

6.7.6 型変換

オブジェクトの型は上位型に変換する(弱める)ことができます。 ( expr :> typexpr) は式 expr の型を typexpr に変換します。 ( expr : typexpr1 :> typexpr2)expr の型を typexpr1 から typexpr2 に変換します。 前者では expr の型を t1t2 に変換するとき、 t1t2 の部分型であっても型変換に失敗することがあります。 現在の実装ではオブジェクトや多相バリアントを含む型略記は二段階しか展開せず、再帰性はクラス型で明示されているものだけ管理されます。 失敗した場合には後者の形式を使わなければなりません。

クラス定義中で、そのクラスの定義する型への変換は恒等関数になります。 これは、この型略記に対応するものがまだ完全には定義されていないからです。