46 votes

Fonctions avec des types de paramètres génériques

J'essaie de comprendre comment définir une fonction qui fonctionne sur plusieurs types de paramètres (par exemple int et int64). D'après ce que je comprends, la surcharge des fonctions n'est pas possible en F# (le compilateur s'en plaint certainement). Prenons par exemple la fonction suivante.

let sqrt_int = function
    | n:int   -> int (sqrt (float n))
    | n:int64 -> int64 (sqrt (float n))

Le compilateur se plaint bien sûr que la syntaxe n'est pas valide (les contraintes de type dans la correspondance de motifs ne sont pas supportées, semble-t-il), mais je pense que cela illustre ce que je voudrais réaliser : une fonction qui opère sur plusieurs types de paramètres et renvoie une valeur du type correspondant. J'ai le sentiment que cela est possible en F# en utilisant une combinaison de types génériques, d'inférence de type et de correspondance de motifs, mais la syntaxe m'échappe. J'ai également essayé d'utiliser l'opérateur : ? (tests de type dynamiques) et la fonction quand dans le bloc de correspondance des motifs, mais cela produit encore toutes sortes d'erreurs.

Comme je suis plutôt novice dans la langue, il se peut que j'essaie de faire quelque chose d'impossible ici, alors faites-moi savoir s'il existe une solution alternative.

62voto

Brian Points 82719

La surcharge est typiquement le problème des langages à inférence de type (du moins lorsque, comme dans F#, le système de type n'est pas assez puissant pour contenir des classes de type). Vous avez un certain nombre de choix dans F# :

  • Utiliser la surcharge sur les méthodes (membres d'un type), auquel cas la surcharge fonctionne comme dans les autres langages .Net (vous pouvez surcharger ad-hoc les membres, à condition que les appels puissent être distingués par le nombre/type de paramètres).
  • Utiliser les contraintes de membres "inline", "^" et statiques pour une surcharge ad-hoc des fonctions (c'est ce que font la plupart des divers opérateurs mathématiques qui doivent fonctionner sur int/float/etc. ; la syntaxe est bizarre, c'est peu utilisé en dehors de la bibliothèque F#)
  • Simuler des classes de types en passant un paramètre supplémentaire de dictionnaire d'opérations (c'est ce que fait INumeric dans l'une des bibliothèques F# PowerPack pour généraliser divers algorithmes mathématiques pour des types arbitraires définis par l'utilisateur).
  • Revenir au typage dynamique (passer un paramètre 'obj', faire un test de type dynamique, lancer une exception d'exécution pour mauvais type).

Pour votre exemple particulier, je me contenterais probablement d'utiliser la surcharge des méthodes :

type MathOps =
    static member sqrt_int(x:int) = x |> float |> sqrt |> int
    static member sqrt_int(x:int64) = x |> float |> sqrt |> int64

let x = MathOps.sqrt_int 9
let y = MathOps.sqrt_int 100L

19voto

Gustavo Points 5670

Ça marche :

type T = T with
    static member ($) (T, n:int  ) = int   (sqrt (float n))
    static member ($) (T, n:int64) = int64 (sqrt (float n))

let inline sqrt_int (x:'t) :'t = T $ x

14voto

Mauricio Scheffer Points 70470

Oui, c'est possible. Jetez un coup d'œil à ce fil de discussion sur les hubFS .

Dans ce cas, la solution serait :

let inline retype (x:'a) : 'b = (# "" x : 'b #)
let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a

Caveat : pas de vérification de type à la compilation. C'est-à-dire sqrt_int "blabla" compile bien mais vous obtiendrez une FormatException au moment de l'exécution.

9voto

Brian Points 82719

Voici une autre façon d'utiliser les contrôles de type d'exécution...

let sqrt_int<'a> (x:'a) : 'a = // '
    match box x with
    | :? int as i -> downcast (i |> float |> sqrt |> int |> box)
    | :? int64 as i -> downcast (i |> float |> sqrt |> int64 |> box)
    | _ -> failwith "boo"

let a = sqrt_int 9
let b = sqrt_int 100L
let c = sqrt_int "foo" // boom

2voto

Joel Mueller Points 14985

Sans vouloir minimiser l'importance des réponses correctes déjà fournies, vous pouvez en fait utiliser les contraintes de type dans le filtrage de motifs. La syntaxe est la suivante :

| :? type ->

Ou si vous voulez combiner la vérification de type et le casting :

| :? type as foo ->

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X