Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Global operator overloading in F#

Tags:

operators

f#

I'm starting down the road of defining my own operators for Cartesian products and matrix multiplication.

With matrices and vectors aliased as lists:

type Matrix = float list list
type Vector = float list

I can write my own initialisation code (and get a Cartesian product into the bargain) by writing

let inline (*) X Y =
    X |> List.collect (fun x -> Y |> List.map (fun y -> (x, y)))

let createVector size f = 
    [0..size - 1] |> List.map (fun i -> f i)

let createMatrix rows columns f =
    [0..rows - 1] * [0..columns - 1] |> List.map (fun i j -> f i j)

So far so good. The problem is that my definition of * wipes out the normal definition, even though my version is only defined for Lists, which don't have their own multiplication operator.

The documentation http://msdn.microsoft.com/en-us/library/vstudio/dd233204.aspx states that "newly defined operators take precedence over the built-in operators". However, I wasn't expecting all of the numerical incarnations to be wiped out -- surely static typing would take care of this?

I know it is possible to define a global operator that respects types, because MathNet Numerics uses * for matrix multiplication. I would also like to go down that road, which would require the typing system to prioritise matrix multiplication over the Cartesian product of Matrix types (which are themselves lists).

Does anyone know how to do this in F#?

like image 840
Rob Lyndon Avatar asked Oct 30 '13 12:10

Rob Lyndon


2 Answers

The idiomatic solution is to define Matrix as a new type and add operators as static members:

type Matrix = 
  | MatrixData of float list list
  static member (*) (MatrixData m1, MatrixData m2) = (...)

Operators defined using let are useful when you know that the definition does not conflict with anything else, or when you're defining the operator locally in a small scope (within a function).

For custom types, it is a good idea to define operators as static members. Another benefit of this approach is that your type will be usable from C# (including the operators).

To do this in your example, I had to change the type definition from type alias (which is not a stand-alone type and so it cannot have its own static members) to a single-case discriminated union, which can have members - but this is probably a good idea anyway.

like image 40
Tomas Petricek Avatar answered Oct 24 '22 06:10

Tomas Petricek


Here is the (non idiomatic) solution:

type Mult = Mult with
    static member inline ($) (Mult, v1: 'a list) = fun (v2: 'b list) -> 
        v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
    static member inline ($) (Mult, v1:'a      ) = fun (v2:'a) -> v1 * v2 :'a

let inline (*) v1 v2 = (Mult $ v1) v2
like image 122
Gus Avatar answered Oct 24 '22 07:10

Gus