オーバーロードはできません?(3)〜暗黙の型変換〜

popup-messageとゆうプロシージャがあります。手軽にポップアップを表示できるため、デバッグなどに活用したりとご存知の方も多いことでしょう。今更説明の必要もないプロシージャだと思われますが、敢えてその使い方を見ていこうと思います。
以下のような使い方ができます。

{popup-message "Curlでリッチなプログラミングを始めます"}

次のように書いても問題なくポップアップが表示されるでしょう。

{popup-message 123}

当然任意のグラフィックを表示することができます。

{popup-message
    {Table columns=2,
        "タイトル", "Curlでリッチプログラミング",
        "URL", "http://d.hatena.ne.jp/giuseppe/"
    }
}


さて、popup-messageのAPIリファレンスを見てみると、そのシグネチャ*1は以下のように定義されています。

public  {popup-message
            message:Graphic,
            title:String = {host-localize "Message"},
            owner:#View = {View.get-default-owner},
            modal?:bool = true,
            cancel?:bool = false,
            ok-label:Label = {hlmessage OK},
            max-width:Distance = 6in,
            large-icon:#Pixmap = null,
            small-icon:#Pixmap = null,
            ...
        }:String

popup-messageの唯一の位置引数はGraphic型と宣言されています。ここで、上に挙げた1つ目と2つ目の例を見てみてください。それぞれString型とint型を引数に指定しています。当然String型はGraphicのサブクラスではありませんし、int型に至ってはプリミティブ型です。curlではプロシージャのオーバーロードを許していないため、当然Stringやintを受け取るpopup-messageが別途用意されているわけではありません。ではなぜStringやintを引数として渡しても正しく動作するのでしょうか?


この仕組みについて説明をする前にもうひとつの例を示します。
以下は、RecordGridのフィルタリングを行うためにfilterプロパティを設定する3種類の方法を示しています。

let rg:RecordGrid = ...
||Nameフィールドが"Jane"のレコードのみ表示します
set rg.filter = {RecordData Name = "Jane"}
||状態がmodifiedのレコードのみ表示します
set rg.filter = RecordState.modified
||Ageフィールドが25より大きいレコードのみ表示します
set rg.filter = {proc {r:Record}:bool
                    {return r["Age"] > 25}
                }

filterプロパティにそれぞれRecordData、RecordState*2、匿名プロシージャが指定されています。しかし、filterプロパティのシグネチャは以下の通りです。

getter public filter:#RecordFilter
setter public filter:#RecordFilter

filterプロパティはアクセサで、そのデータ型は#RecordFilterとなっています。しかし、上記の例で設定しているRecordData、RecordState、匿名プロシージャはいずれもRecordFilterのサブクラスではありません。これはプロパティを設定する例ですが、異なるデータ型を与えてfilterセッターを呼び出しているという点で、popup-messageの場合と同様オーバーロードされているかのように振る舞っている例です。


popup-messageやRecordGridのfilterプロパティにおけるこれらの振る舞いは、暗黙の型変換と呼ばれる仕組みによります。暗黙の型変換とは、異なるデータ型の変数に値を代入した場合に、暗黙的に代入先のデータ型に値を変換する仕組みです。
この暗黙の型変換は、代入先のデータ型がある一定の条件を満たしている場合に実施されます。例えば、popup-messageの引数型であるGraphicクラスには、以下のファクトリが存在します。

public implicit  {Graphic.from-string obj:String}:Graphic

implicitという修飾子がついています。引数が1つのコンストラクタ(またはファクトリ)にこの修飾子がついている場合、その引数型のインスタンスが暗黙的に型変換されるようになります。よって、

{popup-message "Curlでリッチなプログラミングを始めます"}

というpopup-messageの呼び出しは、実行時に引数がString型からGraphic型に暗黙的に変換されていたことにより正しく動作していたのです。RecordGridのfilterプロパティについても同様です。filterプロパティのデータ型であるRecordFilterクラスにimplicitファクトリが用意されています。ヘルプのAPIリファレンスで確認してみてください。


このように、暗黙的な型変換によってオーバーロードに似た多態性を(オーバーロード関数を用意しなくとも)得ることができます。

*1:メソッドやプロシージャの名前と、その引数の種類や数、データ型といった定義情報のこと

*2:Recordの状態を表す列挙型で、original, modified, appended, deleted, new, detachedの6種類が定義されています