ユニバーサル関数呼び出し構文(UFCS)
UFCSは、コンパイラによって自動的に適用される機能だ。これにより、通常の関数でもメンバー関数構文を使用できるようになる。これは、2つの式を比較することで簡単に説明できる。
コンパイラが上記のような式を見つけた場合、variable
で指定された引数で呼び出すことができるfoo
という名前のメンバー関数が存在しない場合、コンパイラは次の式もコンパイルしようと試みる。
この新しい式が実際にコンパイルできる場合、コンパイラは単にその式を受け入れる。その結果、foo()
は明らかに通常の関数であるにもかかわらず、メンバー関数構文で使用できると認識される。
注釈:UFCSは、モジュールスコープで定義された関数のみを考慮する。例えば、ネストされた関数はUFCS構文では呼び出せない。
型と密接に関連する関数は、その型のメンバー関数として定義されることはご存じだろう。これは、型のメンバー関数(およびその型のモジュール)だけがその型のprivate
メンバーにアクセスできるため、カプセル化にとって特に重要だ。
燃料の量を管理するCar
クラスを考えてみよう。
メンバー関数は非常に便利で、時には必要不可欠だが、型に対して動作するすべての関数がメンバー関数であるべきではない。型に対する一部の操作は、特定のアプリケーションに固有であり、メンバー関数としては不適切だ。例えば、自動車が特定の距離を走行できるかどうかを判断する関数は、通常の関数として定義するほうが適切だろう。
これにより、型に関連する関数の呼び出しに不整合が生じる。2つの構文では、オブジェクトが異なる場所に表示される。
UFCSでは、通常の関数をメンバー関数構文で呼び出すことができるようにすることで、この不一致を解消している。
この機能は、リテラルを含む基本的な型でも使用できる。
次の章で見るように、関数に渡す引数がない場合、その関数は括弧なしで呼び出すことができる。この機能も使用すると、上記の式はさらに短くなる。次の3つの文はすべて同等だ。
UFCSは、関数呼び出しが連鎖している場合に特に便利だ。int
スライスを操作する一連の関数で、これを確認しよう。
UFCSを利用せずに通常の構文で記述した場合、これらの関数を3回呼び出す式は、次のプログラムのように記述できる。
値はまず10倍され、次に3で割られ、最後に偶数だけが使用される。
[6, 10, 16]
上記の式の問題点は、multiply
と10
のペアは関連しており、divide
と3
のペアも関連しているにもかかわらず、各ペアの一部が互いに離れて記述されてしまうことだ。UFCSを使用すると、この問題が解決され、実際の演算の順序を反映した、より自然な構文を使用することができる。
一部のプログラマーは、writeln()
のような呼び出しでもUFCSを活用している:
余談だが、上記のプログラム全体は、map()
とfilter()
を使用することで、はるかにシンプルな形で記述することができた。