MikoScript 言語仕様

 関数

●関数定義書式
 関数を定義する書式は、以下のようになります。
    function 関数名( 引数指定 )
    {
        関数定義本体
    }
 関数を定義する簡単な例を、以下に示します。
    function Add( a, b )
    {
        print a, b;
        return a + b;
    }
この関数は、渡された引数 a, b の各値をプリントして、a と b の加算値を返します。
次に、関数定義の書式について、もう少し詳しく説明します。

 「関数名」は、定義される関数の登録先を規定します。これは、単一の識別名(上例)
だけでなく、一般に、「箱の経路」を規定できます。但し、その基点のシステムスコープ
は、モジュールローカルスコープか、グローバルスコープか、または、リレー型関数専用
のスコープに限られます。つまり、「関数名」の先頭に付加できるスコープ規定演算子は、
 ^ :: ' の3種だけに限られます。また、「関数名」の場合のデフォールトスコープは、
モジュールローカルスコープになります。従って、^ 演算子を付けても付けなくても同じ
です。なお、「関数名」内の箱名には連想名も使えますが、その場合、インデックスは、
定数式にする必要があります。以下に「関数名」の例を挙げます。
    Func        モジュールローカルスコープ内の Func という名前の関数
    ::Gamma     グローバルスコープ内の Gamma という名前の関数
    ^A.B        モジュールローカルスコープ内の箱 A 内の B という名前の関数
    ::C.D       グローバルスコープ内のクラス C のメンバー関数 D
    'walk       リレー型関数の walk
    ::Sigma[3]  グローバルスコープ内の Sigma[3] という連想配列名の関数
リレー型関数と連想配列名の関数に関しては、後に説明します。また、クラスのメンバー
関数については、「クラスとインスタンス」の章で説明します。

 関数定義書式内の「引数指定」には、その関数で使われる各引数の名前を規定します。
各引数の名前には、任意の識別名が使えますが、同じ名前があってはいけません。関数に
複数の引数がある場合、各引数の名前をコンマ(,)で区切ります。関数に引数が無い場合、
「引数指定」は、空になります。各引数には、データ型の指定はしません。関数の引数の
個数が不定の場合、「引数指定」を「... 」にします。また、特定できる引数と不特定の
個数の引数の両方がある場合、特定できる各引数の名前を最初に規定して、それ以降を、
「, ... 」にします。
 以下に「引数指定」の例を挙げます。なお、ここで F は、任意の関数名です。
    F()             引数が無い場合
    F( a )          引数が1個の場合
    F( p1, p2 )     引数が2個の場合
    F( X, Y, Z )    引数が3個の場合
    F( ... )        引数が不特定個数の場合
    F( v, ... )     特定引数1個とそれ以外が不特定引数の場合
引数の受け渡しに関しては、後に説明します。

 関数定義書式内の「関数定義本体」には、その関数が呼び出された時に実行する内容を
記述します。これは、通常、一連の実行文になります。この実行に関しては、後述します。
なお、「関数定義本体」は、空にすることもできます。その場合、実質的には何もせずに、
null を返します。

 同じ名前の関数は、たとえ、引数の数が異なっていても、同じスコープ内に、複数登録
することはできません。別のスコープ内であれば、可能です。
 関数定義内に、別の関数定義を入れることはできません。つまり、関数定義は、入れ子
にはできません。但し、「do-with 式」のブロック関数や、後述の「匿名関数」は、使え
ます。

●関数の実行
 関数の実行は、関数をコール(呼び出し)することによって行なわれます。この書式は、
以下のようになります。
    関数名( 引数式の並び )
ここで、「引数式の並び」は、各引数の値を規定する「式」をコンマで区切ったものです。
引数が無い場合は、「引数式の並び」は空になります。なお、このコール形式は標準型で、
本言語ではこれ以外にもいろいろあります。(「関数コール形式」参照)
 関数コールの簡単な例を、以下に示します。
    Add( 3, 4 )
これは、引数に 3 と 4 を指定して、前節の定義例の関数を呼び出したものです。
ちなみに、この実行では、「3, 4」がプリントされ、7 が返されます。
次に、関数実行の動作機構について、もう少し詳しく説明します。

 関数がコールされると、その関数に制御が移行します。その時、まず、渡された引数が、
その関数のローカルスコープに設定されます。次に、「関数定義本体」内の最初の文から
実行が開始されます。以降、関数の実行は、正常終了するか、または、異常終了するまで
続きます。正常終了は、「関数定義本体」内の最後の文の実行を終えるか、return 文の
実行で達成されます。異常終了は、warp 文の実行時や、例外の発生時に起こる強制終了
です。正常終了の場合、制御は、その関数のコール元に戻ります。その時、コール元では、
その関数が返した値を利用できます。

 正常終了でも、異常終了でも、関数を抜ける時は必ず、その関数のローカルスコープが
破棄されます。その時、このスコープ直系の全ての箱が破棄されます。その中に、クラス
のインスタンスがあってデストラクタが定義されていると、そのインスタンスの破棄直前
に、そのデストラクタが呼ばれます。その呼ばれる順は、そのスコープ内でインスタンス
が生成された順とちょうど逆の順になります。つまり、最後に生成されたインスタンスの
デストラクタが最初に呼ばれ、その前に生成されたインスタンスのデストラクタが次に呼
ばれ、以降同様に、最初に生成されたインスタンスのデストラクタが最後に呼ばれます。
これに関しては「クラスとインスタンス」の章で詳しく説明します。

●関数内の局所スコープ
 関数内の局所スコープには、以下の2種類があります。これらについては、「スコープ」
の章で詳しく説明しています。
  ・関数ローカルスコープ
  ・関数スタティックスコープ
このうち、後者は、別節で例を挙げてもう少し詳しく説明します。

●引数の「参照渡し」と「値渡し」
 本言語では、関数に引数を渡す場合、できる限り、参照で渡します。参照で渡せない時
に、実値で渡します。一般に、関数コール時の引数が、箱名単体の場合、関数が受け取る
のは、その箱への参照になります。一方、定数、または、何らかの演算を伴う演算式では、
その評価値になります。

 本言語がこのように「参照渡し」を基本にしているのは、次のような理由に拠ります。
「値渡し」は、引数が数値の場合には、効率的ですが、文字列や構造化データの場合には、
非効率的です。かといって、文字列や構造化データの場合にだけ「参照渡し」にするのも、
やや不自然です。また、箱の中身が数値でも、その箱への参照が必要な場合もあります。
例えば、関数側で、その箱へ代入する場合や、その箱の各種の情報を取得する必要がある
場合などです。それならむしろ、参照で渡せる場合は、「参照渡し」に統一した方が良い
ということになるからです。

 関数側で、受け取った引数の値を読み出すだけなら、「参照渡し」でも「値渡し」でも
大差はありません。しかし、その引数へ値を代入する場合、「参照渡し」では、その代入
先が、その引数自身でなく、その参照先になるので、留意する必要があります。例えば、
    function F( x )  {  print x++;  }
という関数が定義されていたとして、
    v = 1;
    F( v );
    print v;    // ここでは、2 がプリントされる。
の実行では、関数 F のコール時に、変数 v への参照が引数 x に渡されます。そのため、
    print x++;
で、1 がプリントされた後、インクリメントされるのは変数 v になります。結果として、
    1
    2
がプリントされます。

 関数をコールする側で、強制的に「値渡し」にしたい場合は、変数名の前に + 演算子
を付けるか、または、システム組み込みのリレー型関数 'val を使います。例えば、上例
では、F( v ); の所を、
    F( +v );
    F( v'val );
のどちらかにします。ちなみに、このようにして、実行すると、
    1
    1
がプリントされます。なお、+ 演算子を付ける場合、その対象のデータ型は、数値に限定
されます。+ 演算子の対象が数値以外の場合、例外が発生します。一方、'val は、どの
ようなデータ型でも対象とします。構造化データ等で実値にできないものは、参照のまま
になります。

 一方、関数側で、強制的に参照を解消したい場合は、同様に、+ 演算子、または、'val
を、その引数自身に適用して、:= 演算子で、その引数自身に代入し直します。例えば、
    x := +x;
    x := x'val;
のどちらかを行ないます。なお、ここで、= 演算子ではなく、:= 演算子で代入している
のは、= 演算子では、代入先が、引数 x 自身ではなく、引数 x の参照先になってしまう
からです。

●引数のデータ型
 関数をコールする側からは、どんなデータ型でも引数で渡せます。しかし、引数を受け
取る関数側では、必ずしも、どんなデータ型にも対応できるようにはなっているとは限り
ません。そのため、関数のコール側で、その関数の引数がどのようなデータ型に対応でき
るのかを充分に考慮して上で、関数をコールする必要があります。本インタプリタでは、
各関数の引数がどのようなデータ型に対応できるのかは、チェックしません。これは、型
チェックのない言語の欠点でもありますが、プログラム内に逐一型情報を盛り込まなくて
も良いので、プログラミングは、非常に簡便になります。このどちらに重点を置くかは、
その言語の設計思想に依りますが、本言語の場合は、インタプリタなので、簡便さの方を
重視しています。

 引数の型チェックは、プログラムの翻訳時には行なわれませんが、プログラムの実行時
に行なうことはできます。それには、システム組み込みリレー型関数の 'type や 'kind 
を使います。この関数の詳細は、別章で説明しますが、例えば、以下のように使います。
    function T( x )
    {
        if( x'type != "integer"  &&  ( x := x'int ) == null )
            return;

        ・・・・・・
    }
この例では、引数 x のデータ型が整数値でなければ、それを整数値に変換して、引数 x 
自身に代入します。その時、整数値に変換できていなければ、何もせずにリターンします。
整数値に変換できていれば、以降の処理を続けます。なお、引数 x 自身へ代入するのに、
= 演算子ではなく、:= 演算子にしているのは、前節で述べた通りです。

●引数の過不足と省略
 関数で定義されている引数の数よりも、コール側の引数の数の方が少ない場合、不足分
の引数には、null が設定されます。例えば、
    function F( x, y, z )  {  print x, y, z;  }
という関数が定義されていたとして、
    F( 1, 2 );
を実行した場合、関数 F の引数 x, y , z の値は、それぞれ、1, 2, null になります。

 これによって、関数側では、実際に引数が渡されたかどうかを確認できます。そのため、
コール側で、引数を省略して呼び出しても、関数側では、引数が省略された場合の処理を
行なえます。例えば、上例では、引数 z が省略されて呼び出されていますが、関数側で、
引数 z が省略された場合のデフォールト値として 3 を設定したければ、
    if( z == null )  z = 3;
のようにできます。

 引数を省略した関数コールは、どの引数に対しても行なえます。例えば、上例の関数 F
の3個の引数うち、各1個を省略して呼び出す場合は、以下のようになります。
    F( , 2, 3 );
    F( 1, , 3 );
    F( 1, 2,  );
ちなみに、この実行結果は、以下の通りです。
    <null>, 2, 3
    1, <null>, 3
    1, 2, <null>

 関数で定義されている引数の数よりも、コール側の引数の数の方が多い場合、余剰分の
引数は、関数には、渡されません。例えば、上例の関数 F のコールで、
    F( 1, 2, 3, 4 );
としても、第4番目の引数の値は、関数 F には渡されません。
 関数側で、余剰分の引数も受け取りたい場合には、不特定個数の引数として、定義して
おく必要があります。これに関しては、次節で説明します。

●不特定個数の引数の受け渡し
 関数定義書式の「引数指定」内の「... 」の所が、不特定個数の引数に対応するという
ことは、既に述べた通りです。関数がコールされた時、この部分の引数は、
    va_param
という特別な名前の配列に設定されます。この配列は、連想配列で、そのインデックスは、
0, 1, 2, ... の整数値になります。不特定個数の各引数は、それぞれ、
    va_param[0], va_param[1], va_param[2], ... 
に代入されます。va_param 配列内の要素数を取得するには、システム組み込みのリレー
型関数 'count を使って、
    va_param'count
とします。例えば、
    function G( x, ... )  {  ・・・・・  }
という関数が定義されていたとして、
    G( 1, 2, 3, 4 );
でコールした場合、関数 G の引数 x は、1 になり、va_param[0], va_param[1], 
va_param[2] は、それぞれ、2, 3, 4 になります。また、va_param'count は、3 になり
ます。

 関数コール時に、不特定個数の引数が省略されている場合、va_param 配列は、存在し
ません。va_param 配列の存在性は、システム組み込みのリレー型関数 'exist? を使って、
    va_param'exist? 
とすれば、確認できます。例えば、上例の関数 G を
    G( 1 );
のようにコールした場合、va_param 配列は存在しません。その場合、va_param'count は、
-1 になり、va_param'exist? は 偽(0) になります。また、va_param[0] は、例外を発生
します。
 va_param 配列とその中の各要素の存在性に配慮して、各要素を順に使うには、例えば、
以下のようにします。
    for( i = 0 ; va_param[i]'exist? ; i++ )
    {
        // ここで va_param[i] が使える
    }
または、
    N = va_param'count;
    for( i = 0 ; i < N ; i++ )
    {
        // ここで va_param[i] が使える
    }

●名前付き引数の受け渡し
 引数の受け渡しは、上述のように、過不足や省略があったとしても、基本的に、その
順番が、受ける側と渡す側で厳密に対応している必要があります。しかし、次のような
やり方をすれば、その順番に関係なく、その引数の名前で受け渡しすることができます。
それには、各引数の指定を次のようにします。
    引数名: 引数式
これで、「引数名」で指定された引数に「引数式」の値が渡されます。この書式を使った
簡単な例を、以下に示します。
    Add( a: 1, b: 2 )
これは、引数 a に 1 を、b に 2 を指定して、前述の関数をコールします。これは、
Add( 1, 2 ) とコールするのと同じです。この実行では、「1, 2」がプリントされて、
3 が返されます。ちなみに、以下のように、引数の順番を変えてコールしても、同じ
結果になります。
    Add( b: 2, a: 1 )
 このように、引数の名前を使って関数をコールする場合、その関数がどのような名前の
引数で定義されているかを知っている必要があります。もし、引数名を間違えて指定した
場合、正常に実行されない可能性が十分にあります。例えば、上例で、
    Add( x: 1, y: 2 )
のようにコールすると、実行エラーになります。なお、このような引数名の間違いは、
コンパイル時にはチェックされません。そのため、注意が必要です。しかし、その反面、
本言語では、任意の名前の引数を、任意の個数だけ、渡せるようになっています。これを
活用すれば、通常のコンパイラ言語ではできないようなプログラミングもできます。その
場合、関数側でその状況を想定した処理をする必要があります。次の関数はその1例です。
    function ShowArgs()
    {
        T = null;
        do T'up'each with p
        {
            if( p == null )
                return -1;
            print p'name : " = " : p;
        };
    }
この関数は、名前付きで渡された全ての引数の名前とその値をプリントします。例えば、
    ShowArgs( Apple: 5, Orange: 3, Peach: 2 );
とコールすると、次のような結果になります。
    Apple = 5
    Orange = 3
    Peach = 2
 ここで、上記の関数の動作について、簡単に説明しておきます。
この関数の定義本体の実行が開始される時点で、この関数に渡された全ての引数は、
既にこの関数のローカルスコープに設定されています。本件の場合、そこには、
Apple, Orange, Peach という引数が既に設定されています。そこへ、「 T = null; 」
の実行によって、T という変数が追加されます。この関数のローカルスコープは、
T'up で参照され、'each によって、その中にある箱(この場合は変数)が順次列挙
されていきます。その際、p に現対象の箱への参照が設定されています。列挙は、
最後の箱(この場合、変数 T )の番になった時に終了します。

 さて、このような名前付き引数で関数コールができるのは、次の場合に限られます。
  ・ユーザー定義関数の標準型コール
  ・ユーザー定義クラスのコンストラクタの呼び出し(インスタンスの生成)
一方、以下の関数のコールでは、名前付き引数は使えません。
  ・ユーザー定義関数の非標準型コール(リレー型コールなど)
  ・システム組み込み関数
  ・DLL関数
  ・その他

 名前付き引数と通常の引数を混在させた関数コール、例えば、
   Add( a: 3, 4 )
のようなコールはできません。これは、コンパイル時にエラーになります。

 ところで、上記のやり方だけでなく、下記のようにすれば、疑似的に名前付き引数で
関数コールを行なえます。それには、本来の引数をメンバーとする構造体を使います。
例えば、本来の引数が、a, b, c の場合、受ける側(定義関数側)は、これらを直接に
引数リストとするのではなく、これらをメンバーとする構造体を引数とします。以下に
その簡単な例を示します。
    function Func( S )
    {
        print S.a, S.b, S.c ;
    }
 一方、渡す側は、本来の引数を適当な構造体のメンバーとして構成し、その構造体を
引数にして、その関数をコールします。以下にこの簡単な例を示します。
    P.a = 1;  P.b = 2;  P.c = 3;
    Func( P );
このようにすれば、本来の引数である構造体のメンバーの名前が、受け側の想定通りで
さえあれば、その順番は任意です。例えば、上例を次のようにしても、結果は同じです。
    Q ::= {  .c = 3;  .b = 2;  .a = 1;  }
    Func( Q );

●関数の返値と return 文
 関数が返す値を「関数の返値」と言います。関数は、return 文によって、任意の値、
または、任意の箱、あるいは、それらのリストを返すことができます。

 return 文の書式は、以下のようになります。
    return 式;
 この場合、関数の返値は、この「式」の評価値になります。この評価値は、できるだけ
実値になるようにして返されます。但し、限定的に参照で返される場合もあります。これ
は、引数の場合とは対照的です。なぜこのようになっているかというと、返値の対象が、
その関数のローカルスコープ内にある箱になる場合もよくありますが、このスコープは、
関数のリターン時に自動的に消滅してしまうために、その箱への参照を返値にするわけに
はいかないからです。

 return 文によって、返値が具体的にどのように返されるかは、その返値の種別に依存
します。まず、その式の評価値のデータ型が、数値、文字列、または、null になる場合
には、その値自身が返されます。一方、関数になる場合には、その関数への参照が返され
ます。そのほかの場合は、次のようになります。

 関数のローカルスコープ内にある複合箱、または、純粋配列の箱が返される場合、その
箱の実体は、そのスコープが消滅する前に、まずその関数の呼び出し元の関数のローカル
スコープ内の「一時箱」へ移動されます。そして、この一時箱を示す値が返されます。
関数の呼び出し側では、この一時箱を、その関数の返値として使うことになります。この
一時箱は、通常の箱とほとんど同様に扱われます。ただ、これが代入式の右辺になる場合、
その代入は必ず移動代入になります。つまり、代入演算子 =  :=  <- がどれであっても、
一時箱の実体が代入先へ移動されます。これによって、その一時箱は消えます。一時箱は、
このようにして消えなくても、使われなくなった時点で自動的に破棄されます。そのため、
一時箱がいつまでも残存することはありません。

 関数のローカルスコープ外にある複合箱、または、純粋配列の箱が返される場合、その
箱の属するスコープは、関数のリターン時に、消滅することはないので、上述のように、
一時箱へ移動されることはありません。この場合は、その箱の実体を示す値が返されます。
この値は、その箱への言わばポインタ的なもので、その箱の複製ではありません。また、
その箱への参照でもありません。この値は、関数の返値に限ったわけではなく、この種の
箱を扱う式で一般に使われるものです。従って、この関数の返値は、この種の箱が、式に
直接使われている場合と同様の扱いになります。例えば、グローバルスコープ内に G と
いう複合箱、または、純粋配列の箱があったとして、return ::G; を実行して戻ってきた
関数の返値を使うのも、::G を直接単体で使うのも同じことになります。

 箱への参照は、明示的な指示がなければ、参照として返されることはありません。実際
に返される対象になるのは、その参照先の箱です。この箱が具体的にどのように返される
かは、今までに述べた通りです。例えば、受け取った引数をそのままま返す関数
    function f( X )  {  return X;  }
に、グローバルスコープ内にある G という複合箱を渡すと、引数 X は箱 G への参照に
なりますが、返値は、箱 G への参照ではなく、箱 G 自身が対象になります。この場合、
上記の「関数のローカルスコープ外にある複合箱が返される場合」に該当するので、その
箱の実体を示す値が返されます。従って、f( ::G ) を使うのも、::G を直接単体で使う
のも同じことになります。

 箱への参照を返すには、システム組み込みリレー型関数 'ref で明示的に指示する必要
があります。例えば、グローバルスコープ内に G という箱があったとして、
    return ::G'ref;
とすると、箱 G への参照が返ります。なお、'ref の対象が、関数のローカルスコープ内
の箱の場合、返値は null になります。

 return 文の式は、省略できます。その場合の書式は、以下のようになります。
    return;
この場合、関数の返値は、null になります。これは、関数の返値が、存在しないという
ことではありません。これは、
    return null;
と書くのと同じです。

 関数が、return 文を実行せずに、「関数定義本体」内の最後の文の実行を終えて復帰
した場合、関数の返値は、null になります。これは、
    return;
を実行したのと同じです。

 return 文の式は、式のリストにもできます。この場合、各式をコンマで区切り全体を
丸括弧で囲います。例えば、式が3つの場合は、以下のようになります。
    return ( 式, 式, 式 );
このリスト内の各式の評価は、左から右の順に、上記の単式の場合の評価と同じ方式で、
行なわれます。このリスト式の返値は、多重代入でも使えます。なお、この場合のリスト
式は、必ず丸括弧で囲う必要があります。

●関数コール形式
 本言語では、定義された関数をコールする形式が、標準型以外にもいろいろあります。
以下にその一覧を示します。

コール形式         書式          
 標準型       関数名( 引数並び )
 代入型       関数名( 引数並び ) :== 引数
 コマンド型     関数名~ 引数並び ;
 リレー型(後置型) 式 ' 関数名( 引数並び )
 ここで、「関数名」は、コール先の関数を規定します。これは、単一の識別名だけでは なく、一般に、「箱の経路」を規定できます。その基点のシステムスコープは、一般の箱 の場合と同様です。なお、関数定義時の「関数名」では、モジュールローカルスコープか、 グローバルスコープか、リレー型関数専用のスコープかに限られていましたが、コール時 の「関数名」には、そのような特別な制約はありません。また、「関数名」内に連想名が ある場合、そのインデックスには、変数が使えます。  上記の各関数コール形式について、以下の節で説明します。なお、最初の3形式では、 コール例として、次の定義関数を使います。これらは、引数が順に0〜3個になってい ます。
    function F0()           {  ・・・・・  }
    function F1( a )        {  ・・・・・  }
    function F2( a, b )     {  ・・・・・  }
    function F3( a, b, c )  {  ・・・・・  }
●標準型の関数コール形式
 標準型の関数コール形式で、先述の定義関数をコールする場合、それぞれ、次のような
表記になります。これは、基本的に、C 言語等と同じ形式です。
    F0()
    F1( a )
    F2( a, b )
    F3( a, b, c )
 この関数コール形式は、文法的な要素としては「式」になるので、式が使える所では、
どこでもこの表記が使えます。この式の値は、関数が返した値(関数の返値)になります。

●代入型の関数コール形式
 代入型の関数コール形式で、先述の定義関数をコールする場合、それぞれ、次のような
表記になります。なお、このコール形式は、引数のない関数には適用できません。
    F1 :== a
    F2( a ) :== b
    F3( a, b ) :== c
 この関数コール形式では、関数に渡す最後の引数を、:== 演算子の右辺にします。それ
以外の引数は、標準型と同様の関数コール形式にして、同演算子の左辺にします。

 この表記形式は、:== 演算子の左辺に右辺を代入するような感覚で、関数コールを行な
う場合に適します。例えば、何かのパラメータを設定する関数の場合、
   パラメータ設定( 種類, 値 ) ;
というような関数コールよりも、
   パラメータ設定( 種類 ) :== 値 ;
とした方が、イメージ的にマッチします。

 この関数コール形式も、文法的な要素としては「式」になるので、式が使える所では、
どこでもこの表記が使えます。この式の値は、関数が返した値(関数の返値)になります。
なお、:== 演算子の優先度は一番低いので、この関数コール形式全体を丸括弧で囲う必要
がある場合もあります。

●コマンド型の関数コール形式
 コマンド型の関数コール形式で、先述の定義関数をコールする場合、それぞれ、次の
ような表記になります。
    F0~
    F1~ a
    F2~ a, b
    F3~ a, b, c
 この関数コール形式では、関数名を ~ 演算子の左側にし、引数の並びを丸括弧で囲わ
ないで、同演算子の右側にします。

 この表記形式は、感覚的に、関数コールがコマンド実行的な場合に適します。例えば、
  コマンド( パラメータの並び ) ;
というような関数コールよりも、
  コマンド~ パラメータの並び ;
の方が、感覚的に合う場合に使います。

 この関数コール形式も、文法的な要素としては「式」で、この式の値は、関数の返値に
なりますが、この返値を使う場合には、この関数コール形式全体を、丸括弧で囲う必要が
あります。例えば、
    X = ( F2~ a, b ) + c;
    Y = ( F2~ a, ( F1~ b ));
などのようにする必要があります。一方、この関数コール形式の返値を使わないで、それ
単独の文になる場合、例えば、
    F3~ a, b, c ;
では、丸括弧で囲う必要はありません。

●リレー型(後置型)の関数コール形式
 リレー型の関数コール形式は、システム組み込みの各種関数でよく使いますが、一般の
関数でも使えます。一般の関数で、この関数コール形式を使うには、リレー型専用の関数
定義をしておく必要があります。この書式は、本章の最初の「関数定義書式」節で述べた
通りです。
 リレー型専用に定義された関数は、リレー型関数専用のスコープに登録されます。この
スコープは、他のどのスコープにも従属しない独立した1つのシステムスコープで、それ
は、本言語システムの開始から終了まで、存続します。このスコープは、どのスレッドの
どの関数からでも、共通にアクセスできます。その際には、このスコープを特定する「'」
演算子を使います。このスコープには、予め、各種のシステム組み込み関数が登録されて
います。そのため、一般の関数を登録する場合、名前が重複しないように注意する必要が
あります。

 先述の定義関数を、リレー型にすると以下のようになります。なお、ここでは、先述の
関数と区別するために、各関数名は、小文字にしてあります。
    function 'f0()           {  ・・・・・  }
    function 'f1( a )        {  ・・・・・  }
    function 'f2( a, b )     {  ・・・・・  }
    function 'f3( a, b, c )  {  ・・・・・  }
 リレー型の関数コール形式では、これらの関数を、次のような表記でコールします。
    'f0
    a'f1
    a'f2( b )
    a'f3( b, c )
 リレー型の関数コール形式では、関数に渡す引数が無ければ、' 演算子の左項は空で、
右項は関数名だけになります。関数に渡す引数があれば、最初の引数を ' 演算子の左項
にし、それ以外の引数、つまり、第2番目以降の引数は、標準型と同じ形式で、同演算子
の右項にします。この形式では、感覚的に、最初の引数が、主対象になり、第2番目以降
の引数が、副次的になります。これは、C++ 言語等での、クラスのオブジェクトに対して、
メンバー関数をコールする時の関数コール形式に似ています。そのため、このリレー型の
関数コールは、「任意のオブジェクトに対する任意のメンバー関数コール」であるとも言
えます。

 リレー型関数コール形式では、このように、主対象の引数の後に、関数名が来るので、
「後置型関数コール形式」とも言います。これは、関数コール形式として、「リレー」の
機能よりも、「後置」の形態を意識する場合に使います。

 リレー型関数コール形式の本来の役割は、その名の通り、関数コールをリレーすること
にあります。例えば、上記の関数を使って、以下のようにコールできます。
    x 'f1 'f2( p ) 'f3( q, r )
これを、標準型の関数コール形式に置き換えると、以下のようになります。
    f3( f2( f1( x ), p ), q, r )
なお、実際には、リレー型で定義された関数は、標準型の形式では、コールできません。
この表記は、あくまで、説明上のものです。

 さて、この両者の関数コール形式を比較すると、リレー型関数コール形式では、まず、
処理の流れが、表記の順と一致するので、感覚的に分かりやすいという利点があります。
また、引数が1個以下の場合、引数を囲う丸括弧が不要なので、表記が簡素になります。
さらに、上記のような場合でも、各関数の引数を囲う丸括弧が、入れ子にならないので、
分かりやすく簡素な表記になります。

 リレー型の関数コール形式の利点は、他にもいろいろあります。

 リレー型専用に定義された関数は、リレー型関数専用のスコープに登録され、その呼び
出しに専用の演算子(')を使うので、関数名を簡素にしても、他のどのスコープ内の名前
とも競合することなく、しかも、どこからでも共通にアクセスできます。実際、システム
組み込みのリレー型関数には、アルファベット1字だけの関数名もあります。

 一般に、オブジェクト指向プログラミングでは、既存の(或いはライブラリー化された)
クラスに新しいメソッドを追加したい場合には、わざわざ、その派生クラスを定義して、
それに希望のメソッドを追加することになりますが、そのメソッドは、その派生クラスの
インスタンスに対してしか使えません。(ちなみに、本言語がサポートするオブジェクト
指向プログラミングでは、このような面倒なことは一切不要です。その詳細は、「クラス
とインスタンス」の章で述べますが、本節の最後で簡単に触れます。)
 例えば、String というクラスがあって、文字列関連の各種メソッドが用意されている
とします。しかし、そこには、カナをローマ字に変換するメソッドがないので、追加する
ことにします。その場合、String クラスから、たとえば、MyString という派生クラスを
定義し、そこに、必要なメソッド ToRomaji を設けます。この場合、プロパティの追加は
必要ないので、MyString は、ToRomaji メソッドを追加するためだけのクラスになります。
これで、ToRomaji メソッドが使えますが、それは、MyString クラス(とその派生クラス)
のインスタンスに対してだけで、元の String クラスには使えません。このように、まと
もにやれば、かなり面倒なことになります。
 リレー型関数では、任意のオブジェクト(或いはデータ)に対するメソッドを、簡単に
定義することができます。但し、これは、クラスの正式なメソッドではなく、グローバル
関数的にということですが、メソッドのコール形式は、オブジェクト指向風になります。
 例えば、先程の String クラスに対する 'ToRomaji 関数は、
    function 'ToRomaji( s )   // 引数 s は、String オブジェクトを受け取る
    {
        ・・・・・
    }
のように、何の仲介的な手続きもなしに、直接定義できます。
 ちなみに、本言語では、クラスにメソッドを追加する場合、例えば、
    function ::String.ToRomaji()
    {
        ・・・・・
    }
のように簡単にできます。

●匿名関数
 匿名関数は、表面上名前がない(或いは名前が隠された)関数ですが、コールされた時
の実行機能は、通常の関数と同じです。
 匿名関数を定義する書式は、以下のようになります。これは、通常の関数定義書式から
「関数名」を除いた表記に相当します。
    function( 引数指定 )
    {
        関数定義本体
    }
 ここで、「引数指定」と「関数定義本体」は、通常の関数定義書式の場合と同じです。
 匿名関数の定義は、文法上、「式」になります。そのため、変数に代入したり、関数の
引数や、返値にしたりできます。構文上は、「式」が使える所であれば、どこでも、この
匿名関数の定義を記述できますが、意味上、不正になる場合もあります。

 次に、匿名関数を使った例を示します。
    f = function( x ) {  print x;  };
    f( 1.23 );
 これは、受け取った引数の値をプリントする匿名関数を、変数 f に代入して、それを
使って、匿名関数をコールしています。ちなみに、これを実行すると、1.23 がプリント
されます。

 次は、匿名関数を関数の引数で渡す例です。ここでは、配列内の要素を列挙する関数に、
列挙対象の配列と、列挙された各要素を順に受け取る匿名関数を渡します。匿名関数では、
受け取った配列の要素とインデックスをプリントします。
    Season = { "春", "夏", "秋", "冬" };   // 列挙対象の配列

    // 列挙対象の配列と匿名関数(列挙された要素とインデックスをプリントする)を
    //     列挙関数 Enum に渡し、その返値(配列の全要素数)を、変数 n に代入
    n = Enum( Season, function( v, n ) {  print v, n;  } );

    print "要素数 = " : n;
    return;  //(暗黙のメイン関数の終了)

    // 指定 list 内の要素を順に、指定関数 func へ列挙する関数
    function Enum( list, func )
    {
        i = 0;
        for( p := list'first ; p'ref? ; p := list'next )
        {
            func( p, i++ );  // 関数 func に list 内の現要素とインデックスを渡す
        }
        return i;   // 返値はリスト内の全要素数
    }
これを実行した結果は、以下の通りです。
    春, 0
    夏, 1
    秋, 2
    冬, 3
    要素数 = 4

このように、関数を引数で渡す場合、わざわざ、別に定義しておかなくても、匿名関数で
あれば、その引数に直接記述できるので、大変便利です。

 次は、システム組み込みのリレー型関数 'sort と 'each に、匿名関数を渡す例です。
ここでは、整数データが入っている配列の要素を昇順に並べ替えて、プリントしています。
    X = { 25, 84, -10, 36, -97 };
    X'sort( function( a, b ) { return a - b; } );
    X'each( function( v ) {  print v, -;  } );
    print;
これを実行すると、以下の通り、プリントされます。
    -97, -10, 25, 36, 84, 

 ところで、これと同様なことは「do-with 式」でもできますが、両者の違いは、関数
のローカルスコープにあります。do-with 式内のブロック関数内のローカルスコープは、
それよりも外側にあるローカルスコープを引き継ぎますが、匿名関数では、呼び出し側と
匿名関数内部のローカルスコープは、それぞれ独立して存在し、相互の関連はありません。

 次は、匿名関数を返す関数の例です。
    function GetFunc( i )   // 第 i 番目の関数を返す
    {
        switch( i )
        {
          case 1:   return function( x ) {  print "A = " : x ; };
          case 2:   return function( x ) {  print "B = " : x ; };
          case 3:   return function( x ) {  print "C = " : x ; };
        }
    }

    for( i = 1 ; i <= 3 ; i++ )
    {
        f := GetFunc( i );   // 第 i 番目の関数を取得して変数 f に代入
        f( i * 10 );         // 取得した関数をコールする
    }
これを実行すると、以下の通りにプリントされます。
    A = 10
    B = 20
    C = 30

この例では、変数 f に GetFunc( i ) が返した関数を代入していますが、この代入は、
標準代入ではなく、参照代入になっています。この意味については、次節で説明します。

 関数が返した匿名関数をコールするには、上例のように、一旦、適当な変数に代入して
おいて、それを使ってコールしても構いませんが、以下のようにして、変数の仲介なしに、
コールすることもできます。
    [ GetFunc( i ) ]( i * 10 );
なお、本言語の「関数コール形式」では、以下のようにはコールできません。
    GetFunc( i )( i * 10 );     // このコールは不可

●関数の代入と参照によるコール
 一般に、関数を変数に代入すると、その代入先の変数には、その関数の実体が代入され
るのではなくて、その関数箱(その関数の実体に対応する箱)への参照が代入されます。
それは、この方が、高速で、かつ、メモリーの節約になるからです。例えば、
    function CheckPos( x, y )   {  ・・・・・  }
    function ::TextTool.CountWords( text )  {  ・・・・・  }
という関数がある場合、
    f1 = CheckPos;
    f2 = ::TextTool.CountWords;
の実行で、変数 f1 に、モジュールスコープ内の関数 CheckPos への参照が代入されて、
変数 f2 にグローバルスコープ内の関数 TextTool.CountWords への参照が代入されます。

 関数への参照が代入された変数で、その関数を呼び出すには、前述の「関数コール形式」
で、関数名を、その変数名に置き換えるだけです。上例では、以下のようになります。
    f1( 34.5, -87.6 )
    f2( "I am here." )
 関数への参照が代入された変数に対して、標準代入を行なうと、その実質の代入先は、
その参照先の関数箱自体になるので、その箱の中身が変わってしまいます。そうなると、
その関数は2度と呼び出せなくなるので、特に注意が必要です。例えば、
    fx = CheckPos;
    fx = 0;         // CheckPos の関数箱の中身が関数でなくなってしまう!
を実行すると、CheckPos 関数はもはや呼び出せなくなります。また、前節の例で、
    for( i = 1 ; i <= 3 ; i++ )
    {
        f = GetFunc( i );   // ここは標準代入ではダメ(参照代入にすべき)
        f( i * 10 );
    }
とすると、for ループの1回目では、変数 f は新規生成されて、GetFunc(1) の関数への
参照が代入されますが、for ループの2回目では、変数 f の参照先である GetFunc(1) 
の関数箱へ、GetFunc(2) の関数への参照が代入されてしまいます。(この時、変数 f も、
GetFunc(2) の関数への参照になります。)さらに、for ループの3回目では、変数 f の
参照先である GetFunc(2) の関数箱へ、GetFunc(3) の関数への参照が代入されてしまい
ます。(この時、変数 f と GetFunc(1) の関数箱の中身も、GetFunc(3) の関数への参照
になります。)
 このような弊害に配慮して、関数への参照に関連する代入では、常に := 演算子を使う
ようにした方が良いでしょう。

 リレー型で定義された関数は、変数に代入できません。例えば、
    function 'RelayFunc( ・・・ )   {  ・・・・・  }
と定義された関数を、
    f3 = 'RelayFunc;    // これはダメ!
としても、変数 f3 には、RelayFunc への参照は、代入されません。また、仮に代入でき
たとしても、'f3 では、RelayFunc を呼び出せません。

●引数での関数の受け渡し
 定義された関数は、引数で受け渡しできます。引数で渡すには、その関数名をその引数
に指定するだけです。例えば、前節で定義された各関数を、関数 ExecFunc1, ExecFunc2 
に渡すには、以下のようにします。
    ExecFunc1( CheckPos )
    ExecFunc2( ::TextTool.CountWords )
また、前節のように、変数に関数への参照が代入されていると、以下のようにできます。
    ExecFunc1( f1 )
    ExecFunc2( f2 )
 コールされた関数側で、受け取った関数をコールするには、前述の「関数コール形式」
で、関数名を、その引数名に置き換えるだけです。例えば、関数 ExecFunc1, ExecFunc2 
側では、以下のようにします。
    function ExecFunc1( f )
    {
            :
        f( 34.5, -87.6 );    // 受け取った CheckPos 関数をコール
            :
    }

    function ExecFunc2( f )
    {
            :
        f( "I am here." );   // 受け取った ::TextTool.CountWords 関数をコール
            :
    }
 なお、リレー型で定義された関数は、引数で受け渡しできません。

●返値での関数の受け渡し
 定義された関数は、返値で受け渡しできます。関数を返値で返すには、return 文の式
を、その関数名にするだけです。例えば、以下の関数は、引数の値に応じて、前々節の各
関数を返します。
    function ReturnFunc( i )   // 第 i 番目の関数を返す
    {
        switch( i )
        {
          case 1:   return CheckPos;
          case 2:   return ::TextTool.CountWords;
        }
    }
また、前々節のように、変数に関数への参照が代入さていると、以下のようにできます。
    return f1;
    return f2;
 返された関数をコールする方法は、「匿名関数」については既に述べましたが、一般の
関数についても同様です。つまり、返された値を適当な変数に一旦代入しておいて、次に
その変数名を、前述の「関数コール形式」での関数名にしてコールします。例えば、関数
ReturnFunc が返してきた関数は、以下のようにしてコールします。
    f := ReturnFunc(1);
    f( 34.5, -87.6 );       // CheckPos 関数をコール
    g := ReturnFunc(2);
    g( "I am here." );      // ::TextTool.CountWords 関数をコール
また、以下のようにして、変数の仲介なしに、コールすることもできます。
    [ ReturnFunc(1) ]( 34.5, -87.6 );   // CheckPos 関数をコール
    [ ReturnFunc(2) ]( "I am here." );  // ::TextTool.CountWords 関数をコール
 なお、リレー型で定義された関数は、return 文の式に返値として指定できません。

●関数テーブルでの関数コール
 定義された関数を、関数テーブルに登録しておいて、そのインデックスに対応する関数
をコールすることもできます。例えば、
    function FuncA( v )  {  print "FuncA: " : v ;  }
    function FuncB( v )  {  print "FuncB: " : v ;  }
    function FuncC( v )  {  print "FuncC: " : v ;  }

    FuncTable = { FuncA, FuncB, FuncC };   // 関数テーブル

    for( i = 0 ; i < 3 ; i++ )
    {
        FuncTable[i]( i );      // 関数テーブル内の関数をコール
    }
これを実行すると、以下の通り、プリントされます。
    FuncA: 0
    FuncB: 1
    FuncC: 2

 また、関数テーブルを使って、コマンド名に対応する関数をコールすることもできます。
この場合、インデックスがコマンド名の文字列で、要素が実行対象の関数となる連想配列
の関数テーブルを作成しておきます。例えば、以下のようにします。
    CmdTable[ "CmdA" ] := FuncA;
    CmdTable[ "CmdB" ] := FuncB;
    CmdTable[ "CmdC" ] := FuncC;
これで、CmdName という変数に入っているコマンド名に対応する関数をコールするには、
    if( CmdTable[ CmdName ]'exist? )
        CmdTable[ CmdName ]( param );   // コマンド名の関数をコール
    else
        print CmdName : " は、登録されていません!"
のように簡単にできます。なお、上記の param は適当な引数です。

●連想配列名の関数
 連想配列名の関数を定義しておくと、前節の関数テーブルでの関数コールと同様のこと
が、関数テーブルへの登録なしに、行なえます。例えば、
    function Func[0]( v )   {  print "Func[0]: " : v ;  }
    function Func[1]( v )   {  print "Func[1]: " : v ;  }
    function Func[2]( v )   {  print "Func[2]: " : v ;  }

    for( i = 0 ; i < 3 ; i++ )
    {
        Func[i]( i );
    }
これを実行すると、以下の通り、プリントされます。
    Func[0]: 0
    Func[1]: 1
    Func[2]: 2

また、以下のように、インデックスをコマンド名の文字列にすることもできます。
    function CmdFunc[ "CmdA" ]( v )   {  print "Command-A : " : v ;  }
    function CmdFunc[ "CmdB" ]( v )   {  print "Command-B : " : v ;  }
    function CmdFunc[ "CmdC" ]( v )   {  print "Command-C : " : v ;  }
これで、CmdName という変数に入っているコマンド名に対応する関数をコールするには、
    if( CmdFunc[ CmdName ]'exist? )
        CmdFunc[ CmdName ]( param );   // コマンド名の関数をコール
    else
        print CmdName : " は、登録されていません!"
のように簡単にできます。なお、上記の param は適当な引数です。

●関数の再帰呼び出し
 関数は、自分自身を呼び出せます。自分自身の関数を呼び出すことを「再帰呼び出し」
と言います。
 例えば、階乗を求める関数は、再帰呼び出しを使えば、以下のように書けます。
    function factorial( n )     // 階乗を求める関数
    {
        if( n == 1 )
            return 1;
        return n * factorial( n - 1 );    // ここで再帰呼び出し
    }
ちなみに、この関数を使って、
    print  "3! = " : factorial( 3 );
    print  "4! = " : factorial( 4 );
    print  "5! = " : factorial( 5 );
を実行すると、以下の通りプリントされます。
    3! = 6
    4! = 24
    5! = 120

 なお、階乗を求めるには、再帰呼び出しを使わないほうが、効率的なプログラムが書け
ます。この例は、あくまで、説明のためだけのものです。

 次に、再帰呼び出しを使った方が、簡単で効率的になる例を示します。これは、ツリー
構造のデータを、丸括弧の入れ子形式のリストで、プリントする関数です。
    function PrintTree( tree )  // ツリー構造のデータのプリント
    {
        print "( " : -;
        for( p := tree'first ; p'ref? ; p := tree'next )
        {
            if( p'cbox? )         //「箱を入れる箱」?
                PrintTree( p );   // 再帰呼び出し!
            else
                print p : " " : -;
        }
        print ") " : -;
    }
この関数を使って、以下のようなツリー構造のデータをプリントしてみます。
    tree = { 1, 2, { 31, 32, 33 }, 4, { 51, 52, { 531, 532, 533 }, 54 } };

    PrintTree( tree );      // ツリー構造のデータ tree の内容をプリントする
    print ;                 // 最後の改行のプリント
この実行結果は、以下の通りです。
    ( 1 2 ( 31 32 33 ) 4 ( 51 52 ( 531 532 533 ) 54 ) ) 

●関数のスタティックスコープ
 このスコープの一般的な説明は、「スコープ」の章の「関数スタティックスコープ」で
しています。ここでは、例を挙げてもう少し詳しく説明します。
 次の関数は、呼ばれるたびに1ずつ増える整数値を返します。つまり、最初の呼び出し
では 1 を返し、2回目の呼び出しでは 2 を返し、3回目の呼び出しでは 3 を返し・・・
というようになります。
    function Count()
    {
        if( ! @N'exist? )  @N = 0;
        return ++@N;
    }
ここで、@N は、Count という関数のスタティックスコープ内にある N という名前の変数
です。この関数が、最初に呼ばれた時、そのスタティックスコープ内には、何もないので、
@N を生成して、その値を 0 にしておく必要があります。そして、関数を抜ける際に @N 
をカウントアップして、その値を返します。Count() 関数が、2回目に呼ばれた時には、
@N は前回の値を存続しているので、それをカウントアップして返します。以降同様に、
Count() 関数は呼ばれるごとに、@N をカウントアップして返します。ちなみに、
    print Count(), Count(), Count();
を実行すると、「1, 2, 3」とプリントされます。
 なお、上記の Count() 関数は、次のようにすれば、同機能で、少し簡素になります。
    function Count()
    {
        @N'LONG;
        return ++@N;
    }
ここで、'LONG というシステム組み込みのリレー型関数は、対象の箱(変数)が不在の時、
それを新規に生成して値を 0 にします。一方、その箱(変数)が存在する時は、それに 
'LONG の入出力形式を設定しますが、その値はそのままです。
ちなみに、上記の Count() 関数は、次のようにすれば、同機能でさらに簡素にできます。
    function Count()  {  return ++@N'LONG;  }
 次の関数は、渡された文字列を蓄積して、今までに蓄積してきた文字列を返します。
    function StrAcc( s )  {  return @S'C += s;  }
ちなみに、次のように実行すると、「A, AB, ABC」とプリントされます。
    print StrAcc("A"), StrAdd("B"), StrAdd("C");

●関数のメンバースコープ
 一般に、関数がコールされて実行される時のメンバースコープは、その関数が検索され
て見つかったスコープになります。例えば、複合箱内のスコープで検索されてコールされ
る関数の場合をみてましょう。
    C.SetXYZ = function( x, y, z ) {  .X = x;  .Y = y;  .Z = z;  };
では、箱 C の中に、SetXYZ という関数が設定されますが、これを、
    C.SetXYZ( 1, 2, 3 );
としてコールすると、関数 SetXYZ は、箱 C 内で検索されて見つかって実行されるので、
その実行時のメンバースコープは、箱 C 内のスコープになります。そのため、この関数
の実行中の .X, .Y, .Z は、それぞれ、C.X, C.Y, C.Z になります。従って、この後に、
    print C.X, C.Y, C.Z;
を実行すると、「1, 2, 3」とプリントされます。

 この原理は、クラスのメンバー関数が呼ばれる時にも適用されます。これに関しては、
「クラスとインスタンス」で詳述しています。

 ちなみに、モジュールローカルスコープ内で検索されて見つかった関数がコールされた
時のメンバースコープは、モジュールローカルスコープになります。例えば、下例の関数
^SetXYZ の実行中において、.X, .Y. .Z は、それぞれ、モジュールローカルスコープ内の 
^X, ^Y, ^Z になります。
    function ^SetXYZ( x, y, z ) {  .X = x;  .Y = y;  .Z = z;  }
    ^SetXYZ( 1, 2, 3 );
    print ^X, ^Y, ^Z;
これを実行すると、「1, 2, 3」とプリントされます。

 また、同様に、グローバルスコープ内で検索されて見つかった関数がコールされた時の
メンバースコープは、グローバルスコープになります。

●関数のメンバースコープを規定したコール形態
 関数コールの時、その関数の実行時のメンバースコープを、任意の複合箱内のスコープ
に規定することができます。その場合のコール形態は、以下のようになります。
    箱名.[ 関数名 ]( 引数並び )
ここで、「箱名」の箱は、複合箱で、その箱内のスコープが、「関数名」の関数がコール
されて実行される時のメンバースコープになります。この「箱名」は、単一の識別名だけ
でなく、一般に、「箱の経路」を規定できます。その基点のシステムスコープは、一般の
箱の場合と同様です。

 例えば、以下のような関数があったとします。
    function ::PrintXYZ()  {  print .X, .Y, .Z;  }
この関数を使って、以下のように設定された複合箱 C の中身をプリントできます。
    C.X = 1;  C.Y = 2;  C.Z = 3;
    C.[ ::PrintXYZ ]();
ここで、コールされた関数 ::PrintXYZ の実行時のメンバースコープは、複合箱 C 内の
スコープになっています。そのため、この関数の実行中の .X, .Y, .Z は、それぞれ、
C.X, C.Y, C.Z になります。ちなみに、これを実行すると、以下の通りプリントされます。
    1, 2, 3

また、以下のような関数があったとします。
    function ::SetXYZ( x, y, z ) {  .X = x;  .Y = y;  .Z = z;  }
この関数を使って、先程の複合箱 C の中身を、例えば、以下のように変更できます。
    C.[ ::SetXYZ ]( 4, 5, 6 );
ここでのコールも同様に、関数 ::SetXYZ の実行時のメンバースコープは、複合箱 C 内
のスコープになっているので、この関数内の .X, .Y, .Z は、それぞれ、C.X, C.Y, C.Z 
と同等です。ちなみに、この後で、
    C.[ ::PrintXYZ ]();
を実行すると、以下の通りプリントされます。
    4, 5, 6

 上記のコール形態で、「箱名」を省略することもできます。その場合、
    .[ 関数名 ]( 引数並び )
というコール形態なります。この場合、現メンバースコープを継承して関数をコールする
ことになります。つまり、関数コール時点でのメンバースコープが、コールされた関数の
実行時のメンバースコープになります。

 このコール形態を使うと、任意の関数を、各種のオブジェクトのメンバー関数として、
コールできます。これに関しては、「構造体」と「クラスとインスタンス」で説明します。

 また、以下の文のブロック内で、そのメンバースコープを継承して関数を呼び出す場合
にも、このコール形態を使います。
  ・scope 文
  ・構造体設定文
  ・クラス設定文

例えば、
    C ::= {};   // 空の複合箱 C を生成(「構造体」の章参照 )
    scope C     // メンバースコープが一時的に複合箱 C 内になる
    {
        .[ SetXYZ ]( 1, 2, 3 );     // ※
    }
    print C.X, C.Y, C.Z;
    return;

    function SetXYZ( x, y, z ) {  .X = x;  .Y = y;  .Z = z;  }
では、※印のところでコールされた関数 SetXYZ が実行される時のメンバースコープは、
scope C { ... } ブロック内のメンバースコープ、つまり、暗黙のメイン関数のローカル
スコープ内にある複合箱 C 内のスコープになります。この箱 C と SetXYZ 関数との直接
的なスコープの繋がりはありませんが、SetXYZ 関数は、その実行時のメンバースコープ
を通して、箱 C 内のメンバー .X, .Y, .Z にアクセスできています。ちなみに、これを
実行すると、以下の通りプリントされます。
    1, 2, 3

ところで、この例で、※印のところの関数コールを、普通に、
    SetXYZ( 1, 2, 3 );
とすると、この関数の実行時のメンバースコープは、モジュールローカルスコープになる
ので、箱 C 内のメンバーは変更されずに、モジュールローカルスコープ内に X, Y, Z の
箱が生成されてしまいます。

 なお、メンバースコープを継承して、関数から戻ることはありません。つまり、コール
先の関数の実行時にどのようなメンバースコープであっても、その関数から戻った時には、
その関数のコール前と同じメンバースコープになります。例えば、上記の文のブロック内
で、return文を実行した場合も同様です。以下に、この例を示します。
    function f( x, y, z )
    {
        scope ::G   // メンバースコープが一時的に複合箱 G 内になる
        {
            .X = x;  .Y = y;  .Z = z;
            return;   // リターン時のメンバースコープは、::G 内のスコープ
        }
    }

    ::G ::= {};     // 空の複合箱 G を生成
    f( 1, 2, 3 );   // この関数コールの前と後でのメンバースコープは同じ
ここで、関数 f のリターン時のメンバースコープは、::G 内のスコープですが、それが
そのまま継承されるのではなく、この関数から戻った時のメンバースコープは、この関数
のコール前と同じになります。そのため、この後で、
    print .X, .Y, .Z;
を実行すると、エラーになります。ちなみに、
    print G.X, G.Y, G.Z;
の実行では、以下の通りプリントされます。
    1, 2, 3

●関数のローカルスコープへの別スコープの追加/削除
 一般的なスコープの追加/削除については、「スコープ」の章の以下の節で述べました。
   ・スコープの追加
   ・スコープの削除
その時には、システム組み込みのリレー型関数 'AddScope と 'DelScope を使いましたが、
これらの関数コールで、対象の箱を省略すると、そのスコープの追加/削除の対象は、現
実行中の関数のローカルスコープになります。
 例えば、グローバルスコープ内に ::G という複合箱があったとして、現実行中の関数
のローカルスコープに、この箱 ::G 内のスコープを追加するには、
    'AddScope( ::G );
とします。また、この追加したスコープを削除するには、
    'DelScope( ::G );
とします。
 関数のローカルスコープへの別スコープの追加/削除には、'inherit と 'disherit を
使っても構いません。但し、その場合、その追加スコープには、書き込み(代入など)の
アクセスはできません。

 以下に、'AddScope を使ったプログラム例を示します。
    ^G.X = 1;         // モジュールローカルスコープ内に複合箱 ^G を生成
    ^DefValue = -1;   // 不在箱の代替値を -1 にしておく
    f();

    function f()
    {
        'AddScope( ^G );      // 関数ローカルスコープへ ^G 内のスコープを追加
        print X;              // 1 がプリントされる
        X = 2;                // この代入先は、^G.X になる
        Y = 3;                // この代入先は、現関数ローカルスコープ内の Y(新規生成)
        print X, Y;           // 2, 3 がプリントされる
        print ^G.X, ^G.Y;     // 2, -1 がプリントされる( -1 は代替値 )
        // この関数から戻る時、この関数のローカルスコープへ追加されたスコープは
        //  自動的に削除される
    }

●関数のメンバースコープへの別スコープの追加/削除
 システム組み込みのリレー型関数 'inherit と 'disherit の対象を this にすると、
現実行中の関数のメンバースコープに、別スコープを追加/削除できます。
 例えば、グローバルスコープ内に ::G という複合箱があったとして、現実行中の関数
のメンバースコープに、この箱 ::G 内のスコープを追加(継承)するには、
    this'inherit( ::G );
とします。また、この追加したスコープを削除するには、
    this'disherit( ::G );
とします。
 関数のメンバースコープへの別スコープの追加/削除には、'AddScope と 'DelScope 
を使っても構いません。但し、その場合、その追加スコープ(継承元のスコープ)内の
箱に直接、書き込み(代入など)のアクセスができるので注意が必要です。

 以下に、this'inherit を使ったプログラム例を示します。
    ^A.X = 1;    // モジュールローカルスコープ内に複合箱 ^A を生成
    ^B.Y = 2;    // モジュールローカルスコープ内に複合箱 ^B を生成
    ^A.[ f ]();  // 関数 f の実行時のメンバースコープを ^A に規定してコール
     print;

    function f()
    {
        this'inherit( ^B );     // 現メンバースコープへ ^B 内のスコープを継承
        print .X, .Y;            // 1, 2 がプリントされる
        .Y = 3;                  // この実際の代入先は、^B.Y になる
        .Z = 4;                  // この実際の代入先は、^A.Z(新規生成)になる
        print .X, .Y, .Z;        // 1, 3, 4 がプリントされる
        print ^A.X, ^B.Y, ^A.Z;  // 1, 3, 4 がプリントされる
        // この関数から戻る時、この関数のメンバースコープへ継承されたスコープは
        //  自動的に削除される
    }

●関数の情報を取得
 現在実行中の関数やその呼び出し元の関数、あるいは、任意の関数に関する各種の情報
(関数名、ローカルスコープ、ネストの深さなど)は、'func という組み込みのリレー型
関数を使って取得できます。この関数の形式は次のようになります。
    n'func( k )
ここで、n, k には、以下のような値を指定します。なお、以下で、当関数というのは、
情報取得の対象となる関数を指します。

このリレー型関数の対象 n には、次の2種類の指定があります。
(1)当関数が現在実行中の関数から何回前の呼び出し元なのかを示す整数値
    =0: 現実行関数(デフォールト)
    =1: 現実行関数の呼び出し元の関数
    =2: 現実行関数の呼び出し元の呼び出し元の関数
    ・・・・・
(2)当関数の名称(箱名)または当関数への参照
なお、n が省略時は、n = 0 として扱われます。また、n が不正時は、'func の返値は
 null になります。

引数 k は、取得したい情報の種類に応じて、以下の文字列のどれか1つを指定します。
指定文字列取得される情報返値の型
"name"  当関数の名前(主名のみ)   文字列
"full name"  当関数のフルネーム(全経路名)  文字列
"depth"  当関数の呼び出しのネストの深さ  整数値
"local scope"  当関数のローカルスコープ   参照
"member scope"  当関数のメンバースコープ   参照
"static scope"  当関数のスタティックスコープ   参照
なお、この文字列の照合で使用されるのは、最初の4字だけです。このため、例えば、 "full name" の場合、"full" や "full..." などでも、同じ指定として扱われます。 k が省略時は、k = "name" として扱われます。k が不正時は、'func の返値は、null に なります。n の指定が上記(2)の場合、k に指定できるのは、"name", "full name", "static scope" だけです。 この 'func 関数を使った例を、次に示します。
    print 'func( "depth" );   // 現実行関数の呼び出しのネストの深さをプリント
    print 1'func( "name" );   // 現実行関数を呼び出した関数の名前をプリント
次は、呼び出し元の関数のローカルスコープ内の変数を操作する例です。
    FuncX( 3 );
    return;
    
    function FuncX( v )
    {
        A = 1;
        B = 2;
        FuncY();
    }

    function FuncY()
    {
        // 呼び出し元の関数のローカルスコープへの参照を取得して S に設定
        S := 1'func( "local scope" );
    
        // 当ローカルスコープ内の XYZ という変数に 123 の整数値を代入
        S::XYZ = 123;
    
        // 当ローカルスコープ内にある全ての変数の値をプリント
        for( p := S'first ; p'ref? ; p := S'next )
            print p'name : " = " : p;
    }
このスクリプトを実行すると、以下のようにプリントされます。
  v = 3
  A = 1
  B = 2
  XYZ = 123

 ちなみに、呼び出し元の関数のローカルスコープを参照する場合、上例以外にも、
いろいろな書き方ができます。以下にその例を示します。
    print [ 1'func( "local" ) ].Data;   // Data という変数の値をプリント
    [ 1'func( "local" ) ].Var = -234;   // Var という変数に値を設定
 現ローカルスコープに、呼び出し元の関数のローカルスコープを追加するには、
次のようにします。
    'AddScope( 1'func( "local scope" ) );
こうしておけば、呼び出し元の関数のローカルスコープ内の変数が、あたかも、
現ローカルスコープ内にあるかのようにアクセスできます。但し、この場合、
例えば、XYZ という名前の変数が、どちらのスコープ内にも無い時に、
    XYZ = 123;
のような代入を行なうと、この変数 XYZ は、現ローカルスコープ内に新規生成
されます。呼び出し元の関数のローカルスコープ内に新規生成させたい場合は、
上記のようにする必要があります。

 次の例は、対象の関数のスタティックスコープ内の変数を列挙します。
    function F()
    {
        ( @aaa, @bbb, @ccc ) = ( 1, 2, 3 );
    }

    F();
    do F'func("static")'each with p {  print p'name : " = " : p;  };
このスクリプトを実行すると、以下のようにプリントされます。
  aaa = 1
  bbb = 2
  ccc = 3

ちなみに、対象の関数のスタティックスコープ内の変数の値を変更することもできます。
例えば、上記スクリプトの最後に、
    do F'func("static")'each with p {  print p'name : " = " : p *= 10;  };
を追加して実行すると、以下のようにプリントされます。
  aaa = 10
  bbb = 20
  ccc = 30