F Sharp Programming/Operator Overloading
F# : Operator Overloading |
Operator overloading allows programmers to provide new behavior for the default operators in F#. In practice, programmers overload operators to provide a simplified syntax for objects which can be combined mathematically.
Using Operators edit
You've already used operators:
let sum = x + y
Here +
is example of using a mathematical addition operator.
Operator Overloading edit
Operators are functions with special names, enclosed in brackets. They must be defined as static class members. Here's an example on declaring +
operator on complex numbers:
type Complex =
{ Re: double
Im: double }
static member ( + ) (left: Complex, right: Complex) =
{ Re = left.Re + right.Re; Im = left.Im + right.Im }
In FSI, we can add two complex numbers as follows:
> let first = { Re = 1.0; Im = 7.0 };;
val first : Complex
> let second = { Re = 2.0; Im = -10.5 };;
val second : Complex
> first + second;;
val it : Complex = {Re = 3.0;
Im = -3.5;}
Defining New Operators edit
In addition to overloading existing operators, its possible to define new operators. The names of custom operators can only be one or more of the following characters:
!%&*+-./<=>?@^|~
F# supports two types of operators: infix operators and prefix operators.
Infix operators edit
An infix operator takes two arguments, with the operator appearing in between both arguments (i.e. arg1 {op} arg2
). We can define our own infix operators using the syntax:
let (op) arg1 arg2 = ...
In addition to mathematical operators, F# has a variety of infix operators defined as part of its library, for example:
let inline (|>) x f = f x
let inline (::) hd tl = Cons(hd, tl)
let inline (:=) (x : 'a ref) value = x.contents <- value
Let's say we're writing an application which performs a lot of regex matching and replacing. We can match text using Perl-style operators by defining our own operators as follows:
open System.Text.RegularExpressions
let (=~) input pattern =
Regex.IsMatch(input, pattern)
let main() =
printfn "cat =~ dog: %b" ("cat" =~ "dog")
printfn "cat =~ cat|dog: %b" ("cat" =~ "cat|dog")
printfn "monkey =~ monk*: %b" ("monkey" =~ "monk*")
main()
This program outputs the following:
cat =~ dog: false cat =~ cat|dog: true monkey =~ monk*: true
Prefix Operators edit
Prefix operators take a single argument which appears to the right side of the operator ({op}argument
). You've already seen how the !
operator is defined for ref cells:
type 'a ref = { mutable contents : 'a }
let (!) (x : 'a ref) = x.contents
Let's say we're writing a number crunching application, and we wanted to define some operators that work on lists of numbers. We might define some prefix operators in fsi as follows:
> let ( !+ ) l = List.reduce ( + ) l
let ( !- ) l = List.reduce ( - ) l
let ( !* ) l = List.reduce ( * ) l
let ( !/ ) l = List.reduce ( / ) l;;
val ( !+ ) : int list -> int
val ( !- ) : int list -> int
val ( !* ) : int list -> int
val ( !/ ) : int list -> int
> !* [2; 3; 5];;
val it : int = 30
> !+ [2; 3; 5];;
val it : int = 10
> !- [2; 3; 7];;
val it : int = -8
> !/ [100; 10; 2];;
val it : int = 5