Статически разрешаемые параметры типов

Параметр типа статического разрешения — это параметр типа, заменяющийся фактическим типом во время компиляции, а не во время выполнения.

Синтаксис

'type-parameter

До версии 7.0 F#, один из них должен был использовать следующий синтаксис.

^type-parameter

Замечания

В F#существует два разных типа параметров типа. Первый тип — это стандартный параметр универсального типа. Они эквивалентны параметрам универсального типа в других языках .NET. Другой вид статически разрешен и может использоваться только в встроенных функциях.

Статически разрешенные параметры типа в первую очередь полезны в сочетании с ограничениями-членами, которые позволяют указать, что аргумент типа должен иметь определенный член или члены для использования. Невозможно создать это ограничение с помощью обычного параметра универсального типа.

В следующей таблице приведены общие сведения о сходствах и различиях между двумя типами параметров типа.

Функция Универсальный Статически разрешено
Время разрешения Время выполнения Время компиляции
Ограничения элементов Нельзя использовать с ограничениями элементов. Можно использовать с ограничениями элементов.
Создание кода Тип (или метод) со стандартными параметрами универсального типа приводит к генерации одного универсального типа или метода. Создаются несколько экземпляров типов и методов, по одному для каждого типа, который требуется.
Использование с типами Можно использовать для типов. Нельзя использовать для типов.
Использование с встроенными функциями Встроенная функция не может быть параметризована с помощью стандартного параметра универсального типа. Если входные данные не являются полностью универсальными, компилятор F# специализируется на них или, если нет параметров для специализации, дает ошибку. Статически разрешенные параметры типа нельзя использовать для функций или методов, которые не являются встроенными.

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

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

let inline (+@) x y = x + x * y
// Call that uses int.
printfn "%d" (1 +@ 1)
// Call that uses float.
printfn "%f" (1.0 +@ 0.5)

Разрешенный тип (+@) основан на использовании обоих (+) типов и (*), оба из которых приводят к выводу типов ограничений элементов для статически разрешенных параметров типа. Разрешенный тип, как показано в интерпретаторе F#, выглядит следующим образом.

'a -> 'c -> 'd
when ('a or 'b) : (static member ( + ) : 'a * 'b -> 'd) and
('a or 'c) : (static member ( * ) : 'a * 'c -> 'b)

Выходные данные выглядят следующим образом.

2
1.500000

В следующем примере показано использование SRTPs с методами и статическими методами:

type Record =
    { Number: int }
    member this.Double() = { Number = this.Number * 2 }
    static member Zero() = { Number = 0 }
    
let inline double<'a when 'a:(member Double: unit -> 'a)> (x: 'a) = x.Double()    
let inline zero<'a when 'a:(static member Zero: unit -> 'a)> () = 'a.Zero()

let r: Record = zero ()
let doubleR = double r

Начиная с F# 7.0, можно использовать 'a.Zero() вместо необходимости повторять ограничение, как показано в приведенном ниже примере.

Начиная с F# 4.1, можно также указать конкретные имена типов в сигнатурах параметров типа статического разрешения. В предыдущих версиях языка имя типа было выведено компилятором, но не может быть указано в сигнатуре. По состоянию на F# 4.1 можно также указать конкретные имена типов в сигнатурах параметров типа статического разрешения. Ниже приведен пример (не то, что в этом примере необходимо использовать, ^ так как упрощение использования ' не поддерживается):

let inline konst x _ = x

type CFunctor() =
    static member inline fmap (f: ^a -> ^b, a: ^a list) = List.map f a
    static member inline fmap (f: ^a -> ^b, a: ^a option) =
        match a with
        | None -> None
        | Some x -> Some (f x)

    // default implementation of replace
    static member inline replace< ^a, ^b, ^c, ^d, ^e when ^a :> CFunctor and (^a or ^d): (static member fmap: (^b -> ^c) * ^d -> ^e) > (a, f) =
        ((^a or ^d) : (static member fmap : (^b -> ^c) * ^d -> ^e) (konst a, f))

    // call overridden replace if present
    static member inline replace< ^a, ^b, ^c when ^b: (static member replace: ^a * ^b -> ^c)>(a: ^a, f: ^b) =
        (^b : (static member replace: ^a * ^b -> ^c) (a, f))

let inline replace_instance< ^a, ^b, ^c, ^d when (^a or ^c): (static member replace: ^b * ^c -> ^d)> (a: ^b, f: ^c) =
        ((^a or ^c): (static member replace: ^b * ^c -> ^d) (a, f))

// Note the concrete type 'CFunctor' specified in the signature
let inline replace (a: ^a) (f: ^b): ^a0 when (CFunctor or  ^b): (static member replace: ^a *  ^b ->  ^a0) =
    replace_instance<CFunctor, _, _, _> (a, f)

См. также