Универсальные единицы в F #

При написании общих функций на F # я могу использовать члены, определенные в модуле LanguagePrimitives, например, в этой функции просто увеличивает число

let inline increment (x : 'a) =
    x + LanguagePrimitives.GenericOne

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

let inline toNumberWithUnit<[<Measure>] 'u> x =
    x * GenericOne<'u> //that won't work

Это будет иметь тип: 'a ->' a ‹'u>. Является ли это возможным ?


person Alojzy Leszcz    schedule 28.02.2015    source источник


Ответы (1)


Эта подпись была бы недопустимой в .NET, поэтому единственный способ - использовать встроенную функцию F # со статическими ограничениями.

Затем вы можете определить такую ​​функцию:

[<Measure>]
type M = class end

let x = LanguagePrimitives.FloatWithMeasure<M> 2. 

type T<[<Measure>]'M>() =
    static member ($) (T, x) = LanguagePrimitives.FloatWithMeasure<'M> x
    static member ($) (T, x) = LanguagePrimitives.Float32WithMeasure<'M> x
    static member ($) (T, x) = LanguagePrimitives.Int32WithMeasure<'M> x
    // more overloads

let inline NumberWithMeasure x = T() $ x

let a: float<M>   = NumberWithMeasure 2.
let b: float32<M> = NumberWithMeasure 2.0f
let c: int<M>     = NumberWithMeasure 2

Основная проблема при работе с общими числами и единицами измерения заключается в том, что вы получаете те сигнатуры, в которых у вас есть общий тип с параметром типа (a более высокий тип), которые в настоящий момент не поддерживаются в .NET.

ОБНОВЛЕНИЕ

Через некоторое время я тоже столкнулся с этой ситуацией и нашел ответ, который пришел от меня :)

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

Однако вот способ заставить его работать, явно используя оператор $, определенный выше:

[<Measure>] type km
[<Measure>] type miles

type WithMeasure<[<Measure>]'M>() =
    static member ($) (x, T) = LanguagePrimitives.FloatWithMeasure<'M> x
    static member ($) (x, T) = LanguagePrimitives.Float32WithMeasure<'M> x
    static member ($) (x, T) = LanguagePrimitives.Int32WithMeasure<'M> x
    static member ($) (x, T) = LanguagePrimitives.DecimalWithMeasure<'M> x
    static member ($) (x, T) = LanguagePrimitives.Int16WithMeasure<'M> x
    static member ($) (x, T) = LanguagePrimitives.Int64WithMeasure<'M> x
    static member ($) (x, T) = LanguagePrimitives.SByteWithMeasure<'M> x
    // no more overloads


let a: float<km>      = 2.   $WithMeasure()
let b: float32<miles> = 2.0f $WithMeasure()

Может быть способ создать универсальную функцию или универсальную константу, но на данный момент это кажется невозможным с текущей версией F #.

Я попробую с F # 4.1, когда он будет готов.

person Gus    schedule 28.02.2015
comment
Поэтому у меня не может быть общей функции, а скорее последовательность функций, и мне нужно четко указать, какую из них я бы хотел использовать. Что ж - все еще не то, что я хотел бы иметь, но, как вы говорите, это лучшее, что я могу сейчас получить, этого должно быть достаточно. Большое спасибо, сэр :). - person Alojzy Leszcz; 01.03.2015