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 }