Глобальная перегрузка операторов в F#

Я начинаю с определения собственных операторов для декартовых продуктов и умножения матриц.

Матрицы и векторы с псевдонимами в виде списков:

type Matrix = float list list
type Vector = float list

Я могу написать свой собственный код инициализации (и заключить декартово произведение в сделку), написав

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)

Все идет нормально. Проблема в том, что мое определение * стирает нормальное определение, хотя моя версия определена только для списков, которые не имеют своего собственного оператора умножения.

В документации http://msdn.microsoft.com/en-us/library/vstudio/dd233204.aspx говорится, что "вновь определенные операторы имеют приоритет над встроенными операторами". Однако я не ожидал, что все числовые воплощения будут уничтожены - наверняка статическая типизация позаботится об этом?

Я знаю, что можно определить глобальный оператор, который уважает типы, потому что MathNet Numerics использует * для умножения матриц. Я также хотел бы пойти по этому пути, который потребовал бы, чтобы система печатания расставила приоритеты умножения матриц над декартовым произведением Matrix типы (которые сами являются списками).

Кто-нибудь знает, как это сделать в F#?

2 ответа

Решение

Вот (не идиоматическое) решение:

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

Идиоматическим решением является определение Matrix как новый тип и добавьте операторы как статические члены:

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

Операторы, определенные с помощью let полезны, когда вы знаете, что определение не конфликтует ни с чем другим, или когда вы определяете оператор локально в небольшом объеме (внутри функции).

Для пользовательских типов рекомендуется определить операторы как статические члены. Еще одним преимуществом этого подхода является то, что ваш тип будет использоваться из C# (включая операторы).

Чтобы сделать это в вашем примере, мне пришлось изменить определение типа с псевдонима типа (который не является автономным типом и поэтому не может иметь своих собственных статических членов) на однозначное распознаваемое объединение, которое может иметь членов - но это, наверное, хорошая идея в любом случае.

Другие вопросы по тегам