Partilhar via


Parâmetros de tipo resolvidos estaticamente

Um parâmetro de tipo resolvido estaticamente é um parâmetro de tipo que é substituído por um tipo real em tempo de compilação em vez de em tempo de execução.

Sintaxe

'type-parameter

Até a versão 7.0 do F#, era preciso usar a seguinte sintaxe

^type-parameter

Observações

Em F#, há dois tipos distintos de parâmetros de tipo. O primeiro tipo é o parâmetro de tipo genérico padrão. Eles são equivalentes a parâmetros de tipo genéricos em outras linguagens .NET. O outro tipo é resolvido estaticamente e só pode ser usado em funções embutidas.

Os parâmetros de tipo resolvidos estaticamente são principalmente úteis em conjunto com restrições de membro, que são restrições que permitem especificar que um argumento de tipo deve ter um membro ou membros específicos para ser usado. Não há como criar esse tipo de restrição usando um parâmetro de tipo genérico regular.

A tabela a seguir resume as semelhanças e diferenças entre os dois tipos de parâmetros de tipo.

Caraterística Genérica Resolvido estaticamente
Tempo de resolução Tempo de execução Tempo de compilação
Restrições de membros Não pode ser usado com restrições de membro. Pode ser usado com restrições de membros.
Geração de código Um tipo (ou método) com parâmetros de tipo genéricos padrão resulta na geração de um único tipo ou método genérico. Várias instanciações de tipos e métodos são geradas, uma para cada tipo necessário.
Utilização com tipos Pode ser usado em tipos. Não pode ser usado em tipos.
Utilização com funções em linha Uma função embutida não pode ser parametrizada com um parâmetro de tipo genérico padrão. Se as entradas não forem totalmente genéricas, o compilador F# especializa-as ou, se não houver opções para especializar, dá um erro. Os parâmetros de tipo resolvidos estaticamente não podem ser usados em funções ou métodos que não estão embutidos.

Muitas funções da biblioteca principal do F#, especialmente operadores, têm parâmetros de tipo resolvidos estaticamente. Essas funções e operadores estão embutidos e resultam na geração eficiente de código para cálculos numéricos.

Métodos e funções embutidos que usam operadores ou outras funções que têm parâmetros de tipo resolvidos estaticamente, também podem usar parâmetros de tipo resolvidos estaticamente. Muitas vezes, a inferência de tipo infere que tais funções embutidas tenham parâmetros de tipo resolvidos estaticamente. O exemplo a seguir ilustra uma definição de operador que é inferida para ter um parâmetro de tipo resolvido estaticamente.

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)

O tipo resolvido de (+@) é baseado no uso de ambos e (+)(*), ambos os quais causam inferência de tipo para inferir restrições de membros nos parâmetros de tipo resolvidos estaticamente. O tipo resolvido, conforme mostrado no interpretador F#, é o seguinte.

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

A saída é a seguinte.

2
1.500000

O exemplo a seguir ilustra o uso de SRTPs com métodos e métodos estáticos:

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

A partir do F# 7.0, você pode usar 'a.Zero() em vez de ter que repetir a restrição como no exemplo abaixo.

A partir do F# 4.1, você também pode especificar nomes de tipo concretos em assinaturas de parâmetros de tipo resolvidas estaticamente. Em versões anteriores da linguagem, o nome do tipo era inferido pelo compilador, mas não podia ser especificado na assinatura. A partir do F# 4.1, você também pode especificar nomes de tipo concretos em assinaturas de parâmetros de tipo resolvidas estaticamente. Aqui está um exemplo (observe que, neste exemplo, ^ ainda deve ser usado porque a simplificação para usar ' não é suportada):

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)

Consulte também