言語編 |
仕様書の0.07から関数エミュレーション(function emulation)が追加された。関数エミュレーションはPythonで既に導入されている。オブジェクト指向の観点に立てば、各関数はオブジェクトであり、変数に代入でき、関数の戻り値や引数にもなり得る。
あるクラスを関数として使い、自分の関数型を実装させるには次のようなステップを踏む:
callという名前のメソッドを持ったクラスを定義する
()シンタックスで関数として呼び出されたときにそのクラスのインスタンスが何をするかを定義する為にcall()メソッドを実装する
良いスタイルはそのクラスにFunctionクラスを実装させることである
Dartではcallという新しいメソッドを導入して、クラスを関数の代役として使えるようにしている。callはどんな種類の仮パラメタ・リストをも持つことができる。あるクラスの中でcallメソッドを定義することで、関数のエミュレーションが可能となる。例えば:
|
DartPadでこのコードを確認されたい。ここではWannabeFunction(関数になりたい)というクラスの中で、callは引数の3つの文字列を連結して返す演算子として定義されている。この関数をエミュレートしたクラスを使うには、これをインスタンス化して所定の引数をセットして呼び出せば良い。
この例はあまり意味が無く、それなら直接関数を書いたほうが良い。しかしながらこうできることが非常に有用な場合がある。またこれはまたDart言語の設計哲学のコアにもなっている:
オブジェクトで問題となるのはその振る舞いである。オブジェクトaが他のオブジェクトbのそれと互換性を持つ手続き的インターフェイスを持っているとしたら、aはbに置き換え可能である
どの種のオブジェクトのインターフェイスも、常にしかるべく定義された別のオブジェクトによってエミュレートできる
Dartはx(a1, a2, …, an)という式を計算するときに、それが通常の関数の場合はその関数を呼び出す。そうでないときはそれに対しcallを呼び出す。もしxが適切な引数たちでのcall()メソッドに対応していれば、それが呼び出される。そうでないときはnoSuchMethod()が呼び出される。
代入の場合は違ってくる。call()に対応しているあるオブジェクトが関数の型を持ったあるターゲットに代入されるときは、そのオブジェクトは関数のオブジェクトに変換される。より詳しく書けば、式e(callメソッドを持った型の式)がある変数に代入され(或いはパラメタとして渡される、或いは返される、或いはは何かを初期化するために使われる、等)、該ターゲットの型がfunction型であるときは、eは暗示的にe.callに変換される--ある関数オブジェクトをもたらすひとつのティアオフ(さっと片付ける)操作である。
Functionクラスは以下のシグネチュアを持ったstaticなapply()メソッドを定義している:
|
apply()関数により、関数たちを一般的な形で呼べるようにしている。最後の引数は位置的パラメタであり、その呼び出したい関数が名前付きの引数を持っているときはそれが付加される。これらは引数名とその値からなるmapの形で渡される。注意すべき点は、それら引数の名前たちはSymbolクラスのインスタンスを介して記述されることである。
symbolは文字列から生成できる:
|
必要なら、constantなsymbolオブジェクトを作る:
|
constantなsymbolを使うとdart2js(DartコードをJavaScriptに変換するツール)が自分のコードを短縮するのに寄与する。
Dartでは、オブジェクトたちが自分たちのクラスのチェインの中で明示的に定義されていないメソッドたちに対して如何に反応するかを、Objectクラスの中で定義されているnoSuchMethod()メソッドをオーバライドすることでカスタム化できる。noSuchMethod()メソッドの内部で関数エミュレーションをどのように使用するのかの例を示すと:
|
Symbolの#fooがinvocation.memberNameかどうかの条件式の最初のブランチはそのパラメタたちを別の関数に転送したい場合を扱っている。もしbazという関数が名前付き引数をとらないことが分かっていれば、そのコードはFunction.apply(baz, invocation.positionalArguments)となる。次のブランチは単にnoSuchMethod()の標準的な実装への転送で、共通パタンである。noSuchMethod()への引数はInvocationのみである。 Invocationのブール値のプロパティは以下に示す表に従ってそのメソッド呼び出しの文法的書式を特定している:
|
メソッド呼び出しの書式 |
||
|
x.y |
x.y = e |
x.y(...) |
isMethod |
false |
false |
true |
isGetter |
true |
false |
false |
isSetter |
false |
true |
false |
isAccessor |
true |
true |
false |
isMethodであることは非アクセサが検索されていることを意味していると考えてはいけないことは重要であり、これはDartのセマンティクスでは通常のメソッドもゲッタも見つからないときにのみnoSuchMethod()が呼ばれたということを意味しているからである。同様に、isGetterはゲッタが検索されていることを意味しない;もしあるメソッドが存在していればそれがクロージャ化され、返される。
Dartのなかで関数と類似した機能を持ったクラスを定義する手段は:
callという名前のメソッドを持ったクラスを定義する
()構文を介して関数として呼び出される際、自分のクラスのインスタンスが何をするかを定義するためにcall()メソッドを実装する