さて、?<-
はいったん置いておいて、演算子のオーバーロード解決を勉強した。
F# の演算子定義は2通りのパターンがある。
1つは(C#と同様に)クラスなどのスタティックメンバーとして書くもの。
type MyRecord = { x: int; y: int } with static member (+) (left: MyRecord, right: MyRecord) = { x = left.x + right.x; y = left.y + right.y }
メソッドとして書くので、引数はタプルで受け取る。しかし実際には中置演算子として使える。
もう1つは、グローバルレベルで定義するもの。
let (+) (left: MyRecord) (right: MyRecord) = { x = left.x + right.x; y = left.y + right.y }
こちらはF#の関数として書くので、引数はタプルにしない。こちらも中置演算子として使える。
これらの違いは何かというと、演算子がオーバーロード(多重定義)されるかどうかが違う。
グローバルレベルで定義された演算子は、演算子のオーバーロードを隠してしまう。
let (+) (left: MyRecord) (right: MyRecord) = { x = left.x + right.x; y = left.y + right.y } let a = 1 + 2 // コンパイルエラー。MyRecord型の引数が期待されるところにint型の引数を渡している。
演算子のオーバーロードが期待通りに動作するためには、演算子はグローバルレベルで定義するのではなく、引数の型のスタティックメンバーとして定義しておかなければならない。
演算子のオーバーロード解決は、まずグローバルレベルの定義を探し、それが見つからなかった場合だけ、引数の型のスタティックメンバーから適切な演算子定義を探す、という順番になっているからだ。
(続く)