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#?
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With