MikoScript 言語仕様

 参照(リファレンス)

 「参照(リファレンス)」は、対象の箱を指し示すもので、あくまで、対象の箱自身で
はありませんが、対象の箱への参照によるアクセスは、対象の箱の別名(エイリアス)で
あるかのように表記できます。そういう意味で、本言語の「参照」は、C++言語の「リ
ファレンス」と似ていますが、本質的な相違もあります。

●参照の設定
 参照は、箱に格納できます。たとえば、箱 X への参照を、箱 R に格納するには、以下
のように、参照代入演算子 := を使います。
    R := X;
 参照が入っている箱を「参照箱」と呼びます。上例では、箱 R が、参照箱になります。
参照代入演算子 := に関しては、「演算子」の章で説明しています。
 また、参照の設定は、リレー型関数 'ref が返す参照を代入することによってもできま
す。これに関しては、本章で後述します。

●参照先へのアクセス
 参照箱へアクセスすると、その実質の対象は、その参照箱自身ではなく、その参照箱の
参照先の箱になります。たとえば、
    A = "This is A.";
    R := A;     // 箱 R に、箱 A への参照を設定
    print R;    // プリントの対象は、箱 R の参照先の箱 A になる
を実行すると、「This is A.」とプリントされます。また、このあとに、
    A = 1;
    R++;        // ++ の対象は、箱 R の参照先の箱 A になる
を実行すると、箱 A の中身が 1 増えて、2 になります。このとき、参照箱 R の中身は
変わりません。
 このように、参照箱は、実体の箱の別名(エイリアス)としての使い方ができます。

●参照箱自身へのアクセス
 参照箱自身がアクセスの対象になるのは、上述の参照代入演算子 := の左辺(代入先)
になる場合です。たとえば、
    A = "This is A.";
    B = "This is B.";
    P := A;
を実行すると、箱 P に、箱 A への参照が設定されますが、このあとに、
    P := B;
を実行すると、箱 P の参照先の箱 A に、箱 B の中身が代入されるのではなく、箱 P 自
身に、箱 B への参照が代入されます。つまり、参照箱 P の参照先が、箱 A から箱 B に
変わります。ちなみに、上例で、参照代入演算子 := の代わりに、標準代入演算子 = を
使って
    P = B;
を実行すると、箱 P の参照先の箱 A に箱 B の中身が代入されます。

 なお、参照箱自身がアクセスの対象になるのは、この他にも、リレー型関数 'ref? の
場合がありますが、これに関しては、後述します。

●参照箱への参照
 参照箱への参照は、その参照箱の参照先の箱への参照になります。例えば、
    X = "This is X.";
    P1 := X;
    P2 := P1;
    P3 := P2;
を実行すると、箱 P1, 箱 P2, 箱 P3 はともに、箱 X への参照になります。
つまり、参照箱への参照を、箱に格納することはできません。
なお、参照箱以外なら、どの種類の箱への参照も、箱に格納できます。

●参照先と参照箱の削除
 参照先の箱が削除されると、その箱を参照しているどの箱も自動的に削除されます。つ
まり、参照先の実体がない参照箱は、自動的に削除されます。たとえば、
    X = "This is X.";
    P1 := X;
    P2 := X;
を実行すると、箱 P1 と箱 P2 に、箱 X への参照が設定されます。次に、
    delete X;
を実行すると、箱 X が削除されますが、それに伴って、箱 X を参照している箱 P1 と箱
 P2 も自動的に削除されます。また、
    X = "This is X.";
    P1 := X;
    P2 := X;
の次に、
    delete P1;
を実行しても、delete の対象は、参照箱 P1 の参照先の箱 X になるので、箱 X の削除
に伴って、その箱を参照している箱 P1 と箱 P2 も自動的に削除されます。

 参照先の箱は存続させて、参照箱だけを削除するには、参照箱を一旦、空箱などの参照
箱以外の箱にしてから、削除します。たとえば、
    X = "This is X.";
    P := X;
で、箱 P に、箱 X への参照が設定されているときに、箱 X は存続させておいて、箱 P 
だけを削除するには、
    P := null;
として、箱 P 自身を一旦、空箱にします。このとき、箱 P は、もはや参照箱ではなく
なっています。この状態で、
    delete P;
を実行すると、箱 P だけが delete されることになります。

●参照によるスコープの規定
 複合箱(「箱を入れる箱」)への参照は、スコープを規定できます。たとえば、
    A.B.C
は、箱 A の中の箱 B の中の箱 C を示しますが、
    P := A.B;
として、箱 P に、箱 A の中の箱 B への参照を設定しておけば、
    P::C
    P.C
はどちらも、箱 A の中の箱 B の中の箱 C を示します。
 ここで、. と :: はスコープを規定する二項演算子です。この演算子は、「演算子」の
章で説明しています。また、スコープに関しては、「スコープ」の章で説明しています。

●箱の中の参照箱
 箱の中に参照箱を入れることもできます。たとえば、
    A = "This is A.";
    X.P := A;
を実行すると、箱 X の中の箱 P に、箱 A への参照が設定されます。

●参照による関数コール
 関数への参照を参照箱に設定しておくと、その参照箱を使って、対象の関数をコール
することができます。たとえば、
    function fx()
    {
        print "Function fx() called.";
    }
という関数が定義されているときに、
    fp := fx;
を実行すると、箱 fp には、関数 fx への参照が設定されます。次に、
    fp();
を実行すると、参照箱 fp の参照先の関数 fx がコールされて、
    Function fx() called.
とプリントされます。

 なお、関数の実体は複製されないので、標準代入で、
    fp = fx;
としても、箱 fp に、関数 fx への参照が設定されます。しかし、この弊害については、
「関数の代入と参照によるコール」で説明します。

●関数の引数
 関数をコールする時に、その引数を箱名単体にした場合、その箱の中身が渡されるので
はなく、その箱への参照が渡されます。そのため、関数側でその引数に標準代入を行なう
と、実質の代入先は、その引数の参照先の箱になります。これを認識しておかないと、不
本意な結果を招くことになります。例えば、
    function f1( x )
    {
        x++;
    }
という関数が定義されているとして、
    A = 2;
    f1( A );
を実行すると、関数 f1 がコールされた時に、引数 x に箱 A への参照が設定されるので、
演算子 ++ の実質の対象は箱 A になり、最終的に、箱 A の値が 1 増えて 3 になります。

 関数の引数の箱の中身を、参照から、実値に変えたい場合は、その引数自身に、その参
照先の値を代入します。これは、例えば、以下のようにして行ないます。
    function f2( x, y, z )
    {
        x := +x;        // 引数 x の中身は、参照先の数値になる
        y := y'val;     // 引数 y の中身は、参照先の実値になる
        z := z + 3;     // 引数 z の中身は、参照先の数値に 3 を足した数値になる
        ・・・・
    }
ここで、重要なことは、まず、実値に変えたい引数を、参照代入演算子 := の左辺にして
いることです。これによって、代入の対象が、その引数の参照先の箱ではなく、その引数
自身の箱になります。次に、参照代入演算子 := の右辺の評価値が実値になることです。
なお、'val は、対象の実値を返す関数で、本言語にあらかじめ組み込まれているリレー
型関数です。この関数については別途説明します。

 なお、引数が複合箱への参照の場合には、上述の参照から実値への変換は行なえません。
その場合、通常、そのまま使いますが、どうしてもその実体が別途必要なら、その実体の
複製を、別の箱に代入することになります。この操作は、「標準代入」そのものです。

●参照への変換( 'ref )
 リレー型関数 'ref は、本言語にあらかじめ組み込まれている関数で、対象の箱への参
照を返します。たとえば、
    X = "This is X.";
    P = X'ref;
を実行すると、箱 P には、箱 X への参照が代入されます。これは、
    P := X;
と同じようですが、若干違います。それは、箱 P が参照箱の場合です。この場合、
    P = X'ref;
では、箱 P の参照先の箱に、箱 X への参照が代入されます。

 関数 'ref を使うと、参照を関数の返値にできます。たとえば、
    function f()
    {
        return ::A'ref;
    }
と定義された関数は、グローバルスコープ内の箱 A への参照を返します。この参照を返
す関数 f() を使った例を、以下に示します。
    P := f();
を実行すると、箱 P 自身にグローバルスコープ内の箱 A への参照が設定されます。
    ::A = 1;
    f()++;
を実行すると、グローバルスコープ内の箱 A の中身の整数値は 1 増えて 2 になります。
    f() = 3;
を実行すると、グローバルスコープ内の箱 A の中身は、整数値 3 になります。

 なお、リレー型関数 'ref の対象が箱でない場合、その返値は、null になります。
例えば、123'ref は、null になります。

●参照かどうかの確認( 'ref? )
 リレー型関数 'ref? は、本言語にあらかじめ組み込まれている関数で、その対象が、
参照箱か否かを確認する時に使います。この関数は、その対象が、参照箱の場合は 1 を
返し、参照箱でない場合は 0 を返します。たとえば、
    X = "This is X.";
    P := X;
を実行すると、箱 P には、箱 X への参照が代入されますが、ここで、
    P'ref?
の値は、1 になります。一方、
    X'ref?
の値は、0 になります。また、
    123'ref?,  "ABC"'ref?,  null'ref?
等の値も、0 になります。

 通常、参照箱へアクセスすると、実際の対象は、その参照先の箱になりますが、この 
'ref? 関数と、:= の参照代入演算子 の左辺(代入先)に限っては、参照箱へアクセス
しても、その対象は、その参照箱自身になります。

●逆参照アクセス( 'alias )
 リレー型関数 'alias は、本言語にあらかじめ組み込まれている関数で、その対象の
箱への参照箱へアクセスする時に使います。たとえば、前節と同様に、
    X = "This is X.";
    P := X;
を実行すると、箱 P には、箱 X への参照が代入されますが、ここで、
    X'alias
は、箱 X への参照箱、つまり、箱 P を示します。これは、箱 P への参照ではありま
せんが、これで、箱 P へのアクセスができます。まず、これが代入先になる場合、
たとえば、
    X'alias = 123;
のような場合、X'alias は参照ではないので、代入先は、箱 P 自身になります。これは、
    X'alias := 123;
を実行したのと同じです。一方、
    print X'alias;
のような場合、X'alias が示す箱 P の中身は参照なので、実質的なアクセス対象は、
その参照先の箱、つまり、箱 X の中身になります。しかし、
    print X'alias'name;
のような場合、X'alias が示す箱 P の中身とは無関係に、箱の名前が対象になるので、
プリントされるのは、箱 P 自身の名前、つまり、「P」になります。

 対象の箱への参照箱が存在しない場合、または、対象が箱でない場合、'alias は、
null を返します。これを使って、たとえば、
   if( X'alias == null )
       .....
のようにすれば、箱 X を参照している箱があるかないかを確認できます。

 対象の箱への参照箱が複数ある場合、
    X'alias( i )
のようにして、指定番目の参照箱にアクセスできます。ここで、i = 0,1,2, ... は、
第1,2,3,... 番目に、箱 X を参照している参照箱の指定になります。指定番目の
参照箱がなければ、この返値は、null になります。