Partager via


Paramètres de type résolus statiquement

Un paramètre de type résolu statiquement est un paramètre de type qui est remplacé par un type réel au moment de la compilation plutôt qu’au moment de l’exécution.

Syntaxe

'type-parameter

Jusqu’à la version 7.0 de F#, il fallait utiliser la syntaxe suivante :

^type-parameter

Remarques

En F#, il existe deux genres distincts de paramètres de type. Le premier genre est le paramètre de type générique standard. Il est équivalent aux paramètres de type générique dans d’autres langages .NET. L’autre genre est résolu statiquement, et ne peut être utilisé que dans les fonctions inline.

Les paramètres de type résolus statiquement sont principalement utiles conjointement avec les contraintes de membre, qui sont des contraintes qui vous permettent de spécifier qu’un argument de type doit avoir un ou plusieurs membres particuliers pour pouvoir être utilisé. Il n’existe aucun moyen de créer ce genre de contrainte à l’aide d’un paramètre de type générique normal.

Le tableau suivant récapitule les similitudes et les différences entre les deux genres de paramètres de type.

Fonctionnalité Générique Résolu statiquement
Temps de résolution En cours d’exécution Durée de compilation
Contraintes de membre Ne peut pas être utilisé avec des contraintes de membre. Peut être utilisé avec des contraintes de membre.
Génération de code Un type (ou une méthode) avec des paramètres de type générique standard entraîne la génération d’une méthode ou d’un type générique unique. Plusieurs instanciations de types et de méthodes sont générées, une pour chaque type nécessaire.
Utiliser avec des types Peut être utilisé sur les types. Ne peut pas être utilisé sur les types.
Utiliser avec des fonctions inline Une fonction inline ne peut pas être paramétrisée avec un paramètre de type générique standard. Si les entrées ne sont pas entièrement génériques, le compilateur F# les spécialise ou, s’il n’y a aucune option à spécialiser, donne une erreur. Les paramètres de type résolus statiquement ne peuvent pas être utilisés sur des fonctions ou des méthodes qui ne sont pas inline.

De nombreuses fonctions de bibliothèque principale F#, en particulier les opérateurs, ont des paramètres de type résolus statiquement. Ces fonctions et opérateurs sont inline, et entraînent une génération de code efficace pour les calculs numériques.

Les méthodes et fonctions inline qui utilisent des opérateurs, ou utilisent d’autres fonctions qui ont des paramètres de type résolus statiquement, peuvent également elles-mêmes utiliser des paramètres de type résolus statiquement. Souvent, l’inférence de type déduit que ces fonctions inline ont des paramètres de type résolus statiquement. L’exemple suivant illustre une définition d’opérateur qui est déduite comme ayant un paramètre de type résolu statiquement.

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)

Le type résolu de (+@) est basé sur l’utilisation de (+) et (*), qui tous deux ont comme conséquence que l’inférence de type déduit les contraintes de membre sur les paramètres de type résolus statiquement. Le type résolu, comme indiqué dans l’interpréteur F#, est le suivant.

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

La sortie est la suivante.

2
1.500000

L’exemple suivant illustre l’utilisation de paramètres de type résolus statiquement avec des méthodes et des méthodes statiques :

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

À compter de F# 7.0, vous pouvez utiliser 'a.Zero() au lieu d’avoir à répéter la contrainte comme dans l’exemple ci-dessous.

À compter de F# 4.1, vous pouvez également spécifier des noms de type concrets dans les signatures de paramètres de type résolus statiquement. Dans les versions précédentes du langage, le nom de type était déduit par le compilateur, mais ne pouvait pas être spécifié dans la signature. À compter de F# 4.1, vous pouvez également spécifier des noms de type concrets dans les signatures de paramètres de type résolus statiquement. Voici un exemple (notez que dans cet exemple, ^ doit encore être utilisé car la simplification consistant à utiliser ' n’est pas prise en charge) :

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)

Voir aussi