F # создание универсальной функции с универсальными единицами измерения

Я чувствую, что, возможно, борюсь с тем, как F # хочет думать о типах и единицах измерения, но я хотел задать этот вопрос на случай, если я упустил что-то очевидное. Я использую типы и единицы измерения, чтобы обеспечить выравнивание типов во всех операциях. Я хочу определить универсальную функцию tryDivide, которая будет принимать совпадающие типы, но я бегаю по кругу, пытаясь заставить ее работать Вот неуниверсальная версия, которая работает:

[<Measure>] type USD (* US Dollars *)

let tryDivide a b =
    match b > 0M with
    | true -> a / b |> decimal |> Some
    | false -> None


type Cost = decimal<USD>

module Cost =
    let create v : Cost =
        v * 1M<USD>

    let tryDivide (a:Cost) (b:Cost) =
        tryDivide (decimal a) (decimal b)

let costA = Cost.create 10M
let costB = Cost.create 20M

let ratio = Cost.tryDivide costA costB

Проблема в том, что мне нужно определить tryDivide функцию для каждого типа (Пример: Cost.tryDivide). Есть ли способ сделать tryDivide функцию верхнего уровня универсальной для типов и единиц измерения? В идеале это будет любой тип, который на самом деле представляет собой просто decimal<'u>, имеющий единицы измерения. Я пробовал различные сигнатуры функций, но ничего не работает. Если я сделаю что-то вроде следующего, он ограничит ввод до int

let tryDivide (a: 'u) (b: 'u) =
    let x = (decimal a)
    let y = (decimal b)
    match y > 0M with
    | true -> x / y |> decimal |> Some
    | false -> None

Если мой мыслительный процесс в корне неправильный, я хотел бы это услышать. Я вручную кодирую tryDivide для каждого типа, что кажется неэффективным.


person Matthew Crews    schedule 05.01.2018    source источник


Ответы (1)


Я нашел ответ сразу после этого. Вам нужно сделать b > 0M универсальным для юнитов. Для этого вам нужно открыть модуль Microsoft.FSharp.Core.LanguagePrimitives и использовать функцию DecimalWithMeasure. Вот новая версия функции tryDivide:

let tryDivide a b =
    match b > (DecimalWithMeasure 0M) with
    | true -> a / b |> Some
    | false -> None
person Matthew Crews    schedule 05.01.2018