Sobrecarga global del operador en F #

Estoy comenzando a definir mis propios operadores de productos cartesianos y multiplicación de matrices.

Con matrices y vectores con alias como listas:

type Matrix = float list list type Vector = float list 

Puedo escribir mi propio código de inicialización (y obtener un producto cartesiano en la negociación) escribiendo

 let inline (*) XY = X |> List.collect (fun x -> Y |> List.map (fun y -> (x, y))) let createVector size f = [0..size - 1] |> List.map (fun i -> fi) let createMatrix rows columns f = [0..rows - 1] * [0..columns - 1] |> List.map (fun ij -> fij) 

Hasta aquí todo bien. El problema es que mi definición de * borra la definición normal, aunque mi versión solo está definida para listas, que no tienen su propio operador de multiplicación.

La documentación http://msdn.microsoft.com/en-us/library/vstudio/dd233204.aspx establece que “los operadores recién definidos tienen prioridad sobre los operadores incorporados”. Sin embargo, no esperaba que todas las encarnaciones numéricas desaparecieran, ¿seguro que la tipificación estática solucionaría esto?

Sé que es posible definir un operador global que respete los tipos, porque MathNet Numerics usa * para la multiplicación de matrices. También me gustaría ir por ese camino, lo que requeriría que el sistema de tipeo priorice la multiplicación de la matriz sobre el producto cartesiano de los tipos de Matrix (que son ellos mismos las listas).

¿Alguien sabe cómo hacer esto en F #?

Aquí está la solución (no idiomática):

 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 

La solución idiomática es definir Matrix como un nuevo tipo y agregar operadores como miembros estáticos:

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

Los operadores definidos con let son útiles cuando sabes que la definición no está en conflicto con otra cosa, o cuando defines al operador localmente en un ámbito pequeño (dentro de una función).

Para los tipos personalizados, es una buena idea definir operadores como miembros estáticos. Otro beneficio de este enfoque es que su tipo será utilizable desde C # (incluidos los operadores).

Para hacer esto en su ejemplo, tuve que cambiar la definición de tipo de tipo alias (que no es un tipo independiente y por lo tanto no puede tener sus propios miembros estáticos) a una unión discriminada de un solo caso, que puede tener miembros, pero esta es probablemente una buena idea de todos modos.