Подтвердить что ты не робот

Глобальная перегрузка операторов в 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 #?

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

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