[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]
This topic describes how to overload arithmetic operators in a class or record type, and at the global level.
// Overloading an operator as a class or record member.
static member (operator-symbols) (parameter-list) =
method-body
// Overloading an operator at the global level
let [inline] (operator-symbols) parameter-list =
function-body
In the previous syntax, the operator-symbol is one of +, -, *, /, =, and so on. The parameter-list specifies the operands in the order they appear in the usual syntax for that operator. The method-body constructs the resulting value.
Operator overloads for operators must be static. Operator overloads for unary operators, such as + and -, must use a tilde (~) in the operator-symbol to indicate that the operator is a unary operator and not a binary operator, as in the following code:
static member (~-) (v : Vector)
The following code illustrates a vector class that has just two operators, one for unary negation and one for multiplication by a scalar. In the example, two overloads for scalar multiplication are needed because the operator must work regardless of the order in which the vector and scalar appear. Note that when two overloaded operators take the same number of arguments, such as for (*) in the following code, you must add the OverloadID attribute and specify a unique string for each overload. Even then, the argument types must differ; in this case the order of the two parameters differs.
type Vector(x: float, y : float) =
member this.x = x
member this.y = y
static member (~-) (v : Vector) =
Vector(-1.0 * v.x, -1.0 * v.y)
static member (*) (v : Vector, a) =
Vector(a * v.x, a * v.y)
static member (*) (a, v: Vector) =
Vector(a * v.x, a * v.y)
override this.ToString() =
this.x.ToString() + " " + this.y.ToString()
let v1 = Vector(1.0, 2.0)
let v2 = v1 * 2.0
let v3 = 2.0 * v1
let v4 = - v2
printfn "%s" (v1.ToString())
printfn "%s" (v2.ToString())
printfn "%s" (v3.ToString())
printfn "%s" (v4.ToString())
You can overload all the standard operators, but you can also create new operators out of sequences of certain characters. Allowed operator characters are !, $, %, &, *, +, -, ., /, <, =, >, ?, @, ^, | and ~. The ~ character has the special meaning of making an operator unary, and is not part of the operator character sequence.
Depending on the exact character sequence you use, your operator will have a certain precedence and associativity. Associativity can be either left to right or right to left and is used whenever operators of the same level of precedence appear in sequence without parentheses.
The operator characters . and $ do not affect precedence, so that, for example, if you want to define your own version of multiplication that has the same precedence and associativity as ordinary multiplication, you could create operators such as .* or $*.
A table that shows the precedence of all operators in F# can be found in Symbol and Operator Reference (F#).
Overloaded Operator Names
When the F# compiler compiles an operator expression, it generates a method with a compiler-generated name for that operator. This is the name that appears in the Microsoft intermediate language (MSIL) for the method, and also in reflection and IntelliSense. You do not normally need to use these names in F# code.
The following table shows the standard operators and their corresponding generated names.
Operator | Generated name |
|---|
[] | op_Nil |
:: | op_Cons |
+ | op_Addition |
- | op_Subtraction |
* | op_Multiply |
/ | op_Division |
@ | op_Append |
^ | op_Concatenate |
% | op_Modulus |
&&& | op_BitwiseAnd |
||| | op_BitwiseOr |
^^^ | op_ExclusiveOr |
<<< | op_LeftShift |
~~~ | op_LogicalNot |
>>> | op_RightShift |
~+ | op_UnaryPlus |
~- | op_UnaryNegation |
= | op_Equality |
<= | op_LessThanOrEqual |
>= | op_GreaterThanOrEqual |
< | op_LessThan |
> | op_GreaterThan |
|> | op_PipeRight |
<| | op_PipeLeft |
! | op_Dereference |
>> | op_ComposeRight |
<< | op_ComposeLeft |
<@ @> | op_Quotation |
<@@ @@> | op_QuotationUntyped |
+= | op_AdditionAssignment |
-= | op_SubtractionAssignment |
*= | op_MultiplyAssignment |
/= | op_DivisionAssignment |
.. | op_Range |
.. .. | op_RangeStep |
Other combinations of operator characters that are not listed here can be used as operators and have names that are made up by concatenating names for the individual characters from the following table. For example, +! becomes op_PlusBang.
Operator character | Infix or Prefix | Name |
|---|
> | infix | Greater |
< | infix | Less |
+ | infix | Plus |
- | infix | Minus |
* | infix | Multiply |
/ | infix | Divide |
= | infix | Equals |
~ | prefix | Twiddle |
% | infix | Percent |
. | ignored | Dot |
$ | ignored | Dollar |
& | infix | Amp |
| | infix | Bar |
@ | infix | At |
# | infix | Hash |
^ | infix | Hat |
! | prefix | Bang |
? | prefix | Qmark |
( | infix | LParen |
, | infix | Comma |
) | infix | RParen |
[ | infix | LBrack |
] | infix | RBrack |
Infix operators are those that are expected to be placed between the two operands. Prefix operators are expected to be placed in front of an operand or operands, much like a function. Typically, the first character of the operator determines if the operator is an infix or a prefix operator. The operator character . and $ are not used to determine whether an operator is an infix operator or a prefix operator; instead, the next character in the operator is used to determine this.
The following code illustrates the use of operator overloading to implement a fraction type. A fraction is represented by a numerator and a denominator. The function hcf is used to determine highest common factor which is used to reduce the fractions.
// Determine the highest common factor between
// two positive integers, a helper for reducing
// fractions.
let rec hcf a b =
if a = 0u then b
elif a<b then hcf a (b - a)
else hcf (a - b) b
// type Fraction: represents a positive fraction
// (positive rational number).
type Fraction =
{
// n: Numerator of fraction
n : uint32
// d: Denominator of fraction
d : uint32
}
// Produce a string representation. If the
// denominator is "1", then don't display it.
override this.ToString() =
if (this.d = 1u)
then this.n.ToString()
else this.n.ToString() + "/" + this.d.ToString()
// Add two fractions.
static member (+) (f1 : Fraction, f2 : Fraction) =
let n_temp = f1.n * f2.d + f2.n * f1.d
let d_temp = f1.d * f2.d
let hcf_temp = hcf n_temp d_temp
{ n = n_temp / hcf_temp; d = d_temp / hcf_temp }
// Adds a fraction and a positive integer.
static member (+) (f1: Fraction, i : uint32) =
let n_temp = f1.n + i * f1.d
let d_temp = f1.d
let hcf_temp = hcf n_temp d_temp
{ n = n_temp / hcf_temp; d = d_temp / hcf_temp }
// Adds a positive integer and a fraction.
static member (+) (i : uint32, f2: Fraction) =
let n_temp = f2.n + i * f2.d
let d_temp = f2.d
let hcf_temp = hcf n_temp d_temp
{ n = n_temp / hcf_temp; d = d_temp / hcf_temp }
// Subtract one fraction from another.
static member (-) (f1 : Fraction, f2 : Fraction) =
if (f2.n * f1.d > f1.n * f2.d)
then failwith "This operation results in a negative number, which is not supported."
let n_temp = f1.n * f2.d - f2.n * f1.d
let d_temp = f1.d * f2.d
let hcf_temp = hcf n_temp d_temp
{ n = n_temp / hcf_temp; d = d_temp / hcf_temp }
// Multiply two fractions.
static member (*) (f1 : Fraction, f2 : Fraction) =
let n_temp = f1.n * f2.n
let d_temp = f1.d * f2.d
let hcf_temp = hcf n_temp d_temp
{ n = n_temp / hcf_temp; d = d_temp / hcf_temp }
// Divide two fractions.
static member (/) (f1 : Fraction, f2 : Fraction) =
let n_temp = f1.n * f2.d
let d_temp = f2.n * f1.d
let hcf_temp = hcf n_temp d_temp
{ n = n_temp / hcf_temp; d = d_temp / hcf_temp }
// A full set of operators can be quite lengthy, for example,
// consider operators that support other integral data types,
// with fractions, on the left side and the right side for each.
// Also consider implementing unary operators.
let fraction1 = { n = 3u; d = 4u }
let fraction2 = { n = 1u; d = 2u }
let result1 = fraction1 + fraction2
let result2 = fraction1 - fraction2
let result3 = fraction1 * fraction2
let result4 = fraction1 / fraction2
let result5 = fraction1 + 1u
printfn "%s + %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result1.ToString())
printfn "%s - %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result2.ToString())
printfn "%s * %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result3.ToString())
printfn "%s / %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result4.ToString())
printfn "%s + 1 = %s" (fraction1.ToString()) (result5.ToString())
// Output:
3/4 + 1/2 = 5/4
3/4 - 1/2 = 1/4
3/4 * 1/2 = 3/8
3/4 / 1/2 = 3/2
3/4 + 1 = 7/4
Operators at the Global Level
You can also define operators at the global level. The following code defines an operator +?.
let inline (+?) (x: int) (y: int) = x + 2*y
printf "%d" (10 +? 1)
The output of the above code is 12.
You can redefine the usual arithmetic operators this way, since the scoping rules for F# mean that newly defined operators take precedence over the built-in operators.
The keyword inline is often used with global operators, which are often small functions that are best integrated into the calling code. Making operator functions inline also lets them work with statically resolved type parameters to produce statically resolved generic code. For more information, see Inline Functions (F#) and Statically Resolved Type Parameters (F#).
Concepts