F# は OCaml と違って演算子をオーバーロード (多重定義) することができます。
オーバーロードの構文を以下に示します。
static member (op) (args1: type1, args2: type2) = 式
クラスやレコードなどで演算子を多重定義します。static は必ず付けてください。演算子 op が二項演算子の場合、引数は 2 つ必要になります。単項演算子の場合、op の前に ~ を付けて、引数は 1 つだけになります。
レコードは { ... } の後ろで member を定義することができます。
type record_name = { フィールド名 : 型式 ... } member ... ... static member ... ...
簡単な例として、平面座標を表すレコード point を定義し、それをベクトルとみなして和 (+)、差 (-)、スカラー倍 (*)、内積 (*) を計算する演算子を多重定義してみましょう。プログラムは次のようになります。
リスト : レコード point (point.fsx) type point = { x: float y: float } // ベクトルの和と差 static member ( + ) (p1: point, p2: point) = {x = p1.x + p2.x; y = p1.y + p2.y } static member ( - ) (p1: point, p2: point) = {x = p1.x - p2.x; y = p1.y - p2.y } // スカラー倍 static member ( * ) (k: float, p: point) = {x = k * p.x; y = k * p.y } static member ( * ) (p: point, k: float) = {x = k * p.x; y = k * p.y } // 内積 static member ( * ) (p1: point, p2: point) = p1.x * p2.x + p1.y * p2.y
簡単な実行例を示します。
> #load "point.fsx";; ... 略 ... > open Point;; > let p1 = {x = 1.0; y = 1.0};; val p1: point = { x = 1.0 y = 1.0 } > let p2 = {x = 2.0; y = 3.0};; val p2: point = { x = 2.0 y = 3.0 } > p1 + p2;; val it: point = { x = 3.0 y = 4.0 } > p2 - p1;; val it: point = { x = 1.0 y = 2.0 } > p1 - p2;; val it: point = { x = -1.0 y = -2.0 } > 2.0 * p2;; val it: point = { x = 4.0 y = 6.0 } > p2 * 3.0;; val it: point = { x = 6.0 y = 9.0 } > p1 * p2;; val it: float = 5.0 > p2 * p2;; val it: float = 13.0
クラスで定義する場合は次のようになります。
リスト : クラス Point type Point(x0: float, y0: float) = let x = x0 let y = y0 member this.X with get() = x member this.Y with get() = y // ベクトル演算 static member ( + ) (p1: Point, p2: Point) = new Point(p1.X + p2.X, p1.Y + p2.Y) static member ( - ) (p1: Point, p2: Point) = new Point(p1.X - p2.X, p1.Y - p2.Y) // スカラー倍 static member ( * ) (k: float, p: Point) = new Point(k * p.X, k * p.Y) static member ( * ) (p: Point, k: float) = new Point(p.X * k, p.Y * k) // 内積 static member ( * ) (p1: Point, p2: Point) = p1.X * p2.X + p1.Y * p2.Y
> #load "point.fsx";; ... 略 ... > open Point;; > let p1 = new Point(1.0, 1.0);; val p1: Point > let p2 = new Point(2.0, 3.0);; val p2: Point > p1 + p2;; val it: Point = FSI_0002.Point+Point {X = 3.0; Y = 4.0;} > p1 - p2;; val it: Point = FSI_0002.Point+Point {X = -1.0; Y = -2.0;} > p2 - p1;; val it: Point = FSI_0002.Point+Point {X = 1.0; Y = 2.0;} > 2.0 * p2;; val it: Point = FSI_0002.Point+Point {X = 4.0; Y = 6.0;} > p2 * 3.0;; val it: Point = FSI_0002.Point+Point {X = 6.0; Y = 9.0;} > p1 * p2;; val it: float = 5.0 > p2 * p2;; val it: float = 13.0
F# は let でグローバルな演算子を定義することができます。
let [inline] (op) (args1: type1) (args2: type2) = 式
演算子 op が二項演算子の場合、引数は 2 つ必要になります。オーバーロードとは違って、カリー化関数として定義することに注意してください。単項演算子の場合、op の前に ~ を付けて、引数は 1 つだけになります。
なお、この方法で既存の演算子を多重定義することはできません。let の定義が優先されるので、既存の演算子を再定義する、もしくは新しい演算子を定義するときに使用してください。
簡単な例を示しましょう。
> type point = {x: float; y: float};; type point = { x: float y: float } > let (+/) (p1: point) (p2: point) = {x = p1.x + p2.x; y = p1.y + p2.y};; val (+/) : p1: point -> p2: point -> point > let (-/) (p1: point) (p2: point) = {x = p1.x - p2.x; y = p1.y - p2.y};; val (-/) : p1: point -> p2: point -> point > let p1 = {x = 1.0; y = 1.0};; val p1: point = { x = 1.0 y = 1.0 } > let p2 = {x = 2.0; y = 3.0};; val p2: point = { x = 2.0 y = 3.0 } > p1 +/ p2;; val it: point = { x = 3.0 y = 4.0 } > p1 -/ p2;; val it: point = { x = -1.0 y = -2.0 } > p2 -/ p1;; val it: point = { x = 1.0 y = 2.0 }