Operator overloading is a feature in F# which allows you to overload an arithmetic operator or define a custom operator in a class or record type.
To overload an operator as a class or record member, add static member to the type definition as follows:
static member (+) (a : Point, b: Point) =
// method body
Point
and would like to define a +
operator which would combine two Point
instances into a list.+
should also work with a list, meaning [p1; p2] + p3
should return list [p1; p2; p3]
, where p1
, p2
and p3
are all instances of Point
type.> type Point =
{
x: int
y: int
} with
static member (+) (a: Point, b: Point) = [a; b]
static member (+) (a: Point, b: Point list) = b @ [a]
static member (+) (a: Point list, b: Point) =
// reusing operator overload#2 above
b + a
;;
type Point =
{
x: int
y: int
}
static member (+) : a: Point list * b: Point -> Point list + 2 overloads
> let a = {x = 2; y = 3} ;;
val a: Point = { x = 2
y = 3 }
> let b = {x = 8; y = 10} ;;
val b: Point = { x = 8
y = 10 }
> let c = a + b ;;
val c: Point list = [{ x = 2
y = 3 }; { x = 8
y = 10 }]
> c + a ;;
val it: Point list = [{ x = 2
y = 3 }; { x = 8
y = 10 }; { x = 2
y = 3 }]
> a + c ;;
val it: Point list = [{ x = 2
y = 3 }; { x = 8
y = 10 }; { x = 8
y = 10 }]
static member (+) (a: Point, b: Point) = [a; b]
combines two Point
instances into a list.static member (+) (a: Point, b: Point list)
for supporting [p1; p2] + p3
operationstatic member (+) (a: Point list, b: Point)
for supporting p3 + [p1; p2]
operation+
and -
are also known as infix operator because they are expected to be placed between two operands.x + y
, x
and y
are operands and +
is an operator.No.
You can try it by extending existing type but the compiler will show a message saying Extension members cannot provide operator overloads
:
> type Point =
{
x: int
y: int
}
;;
> type Point with
static member (+) (a: Point, b: Point) =
{x = a.x + b.x; y = a.y + b.y}
;;
static member (+) (a: Point, b: Point) =
-------------------^
stdin(238,20): warning FS1215: Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead.
type Point with
static member (+) : a: Point * b: Point -> Point
One workaround for this is to create a wrapper type around the existing type:
> type PointExt (p: Point) =
member this.point = p
static member (+) (a: PointExt, b: PointExt) =
PointExt({
x = a.point.x + b.point.x
y = a.point.y + b.point.y
})
;;
type PointExt =
new: p: Point -> PointExt
static member (+) : a: PointExt * b: PointExt -> PointExt
member point: Point
> let a = PointExt({x = 2; y = 3}) ;;
val a: PointExt
> let b = PointExt({x = 10; y = 12}) ;;
val b: PointExt
> a + b ;;
val it: PointExt = FSI_0093+PointExt {point = { x = 12
y = 15 };}
>>=
and ~~~
.!
, $
, %
, &
, *
, +
, -
, .
, /
, <
, =
, >
, ?
, @
, ^
, |
, and ~
+@
which will add x
and y
values of 2 Point
instances and return a new Point
instance:> type Point =
{
x: int
y: int
}
static member (+@) (a: Point, b: Point) =
{ x = a.x + b.x; y = a.y + b.y }
;;
type Point =
{
x: int
y: int
}
static member (+@) : a: Point * b: Point -> Point
> let a = {x = 2; y = 3} ;;
val a: Point = { x = 2
y = 3 }
> let b = {x = 8; y = 10} ;;
val b: Point = { x = 8
y = 10 }
> a +@ b ;;
val it: Point = { x = 10
y = 13 }
!
or ~
symbol, followed by any number of allowed operator symbols.!!
and ~~
unary operators on Point
type which negate x
and y
values:> type Point =
{
x: int
y: int
}
static member (!!) (p: Point) =
{ x = p.x * -1; y = p.y * -1 }
static member (~~) (p: Point) =
{ x = p.x * -1; y = p.y * -1 }
;;
type Point =
{
x: int
y: int
}
static member (!!) : p: Point -> Point
static member (~~) : p: Point -> Point
> let point = { x = 10; y = 20 } ;;
val point: Point = { x = 10
y = 20 }
> !! point ;;
val it: Point = { x = -10
y = -20 }
> ~~ point ;;
val it: Point = { x = -10
y = -20 }
+
is op_Addition
. You can use op_Addition
instead of +
:> type Point =
{
x: int
y: int
}
static member op_Addition (a: Point, b: Point) =
{ x = a.x + b.x; y = a.y + b.y }
;;
type Point =
{
x: int
y: int
}
static member (+) : a: Point * b: Point -> Point
op_Addition
as +
operator overload.For full list of standard operator names, check out table A.
!@
is made by joining compiler-assigned names for each symbol and prefix it with op_
.!
and @
are Bang
and At
, so the name of !@
would be op_BangAt
.You can define operators at global level using let
keyword:
> let inline (^+*+^) (x: int) (y: int) = x*x + 2*x*y + y*y ;;
val inline (^+*+^) : x: int -> y: int -> int
> 5 ^+*+^ 9 ;;
val it: int = 196
inline
keyword is often using when defining global operators.5 ^+*+^ 9
with function body after substitution: 5*5 + 2*5*9 + 9*9
.inline
also enables them to work with statically resolved type parameters to produce statically resolved generic code. Example:> let inline (+@) x y = x + x * y ;;
val inline (+@) :
x: ^a -> y: ^c -> ^d
when ( ^a or ^b) : (static member (+) : ^a * ^b -> ^d) and
( ^a or ^c) : (static member ( * ) : ^a * ^c -> ^b)
> printfn "%d" (1 +@ 1) ;;
2
val it: unit = ()
> printfn "%f" (1.0 +@ 0.5) ;;
1.500000
val it: unit = ()
inline
keyword above, the compiler would infer the type of +@
as val (+@) : x: int -> y: int -> int
Generated names for standard operators
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_Dynamic |
?<- | op_DynamicAssignment |
|> | 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 |
* Quotation symbols can't be used as an operator, but for some reason F# decided to give a special name for it. Relevant StackOverflow question.
Names of symbols allowed as operator characters:
Operator character | Name |
---|---|
> | Greater |
< | Less |
+ | Plus |
- | Minus |
* | Multiply |
/ | Divide |
= | Equals |
~ | Twiddle |
$ | Dollar |
% | Percent |
. | Dot |
& | Amp |
| | Bar |
@ | At |
^ | Hat |
! | Bang |
? | Qmark |
( | LParen |
, | Comma |
) | RParen |
[ | LBrack |
] | RBrack |
© 2022 Sumeet Das