Fonctions avec types de parameters génériques

J’essaie de comprendre comment définir une fonction qui fonctionne sur plusieurs types de parameters (par exemple, int et int64). Si je comprends bien, la surcharge de fonctions n’est pas possible dans F # (certainement le compilateur se plaint). Prenons par exemple la fonction suivante.

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

Bien sûr, le compilateur se plaint que la syntaxe est invalide (les contraintes de type dans la correspondance de modèle ne sont pas supscopes, bien que cela semble être le cas), mais je pense que cela illustre bien ce que je voudrais faire: une fonction type. J’ai l’impression que cela est possible dans F # en utilisant une combinaison de types génériques / inférence de type / correspondance de modèle, mais la syntaxe m’a échappé. J’ai aussi essayé d’utiliser le:? des clauses d’opérateur (tests de type dynamic) et de date dans le bloc de correspondance de modèle, mais cela produit toujours des erreurs de tous types.

Comme je suis plutôt nouveau dans le langage, je peux très bien essayer de faire quelque chose d’impossible ici, alors s’il vous plaît laissez-moi savoir s’il existe une solution alternative.

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

  • Utilisez la surcharge sur les méthodes (membres d’un type), auquel cas la surcharge fonctionne comme dans les autres langages .Net (vous pouvez append des membres de surcharge ad-hoc, les appels pouvant être distingués par le nombre / type de parameters)
  • Utilisez “inline”, “^” et les contraintes de membres statiques pour une surcharge ad hoc sur les fonctions (c’est ce que la plupart des opérateurs mathématiques qui doivent travailler sur int / float / etc; la syntaxe est bizarre, c’est peu utilisé en dehors de la bibliothèque F #)
  • Simulez des classes de type en passant un paramètre supplémentaire du 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)
  • Retourner à la saisie dynamic (passer un paramètre ‘obj’, faire un test de type dynamic, lancer une exception à l’exécution pour le type incorrect)

Pour votre exemple particulier, je voudrais probablement utiliser la surcharge de méthode:

 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 

Cela 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 

Il utilise des contraintes statiques et une surcharge, ce qui effectue une recherche au moment de la compilation sur le type de l’argument.

Les contraintes statiques sont automatiquement générées en présence d’un opérateur (opérateur $ dans ce cas) mais il peut toujours être écrit à la main:

 type T = T with static member Sqr (T, n:int ) = int (sqrt (float n)) static member Sqr (T, n:int64) = int64 (sqrt (float n)) let inline sqrt_int (x:'N) :'N = ((^T or ^N) : (static member Sqr: ^T * ^N -> _) T, x) 

Plus à ce sujet ici .

Oui, cela peut être fait. Jetez un oeil à ce thread 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 

Attention : pas de vérification de type à la compilation. C’est-à-dire que sqrt_int "blabla" comstack sqrt_int "blabla" mais vous obtiendrez une exception FormatException à l’exécution.

Voici une autre façon d’utiliser les vérifications de type à l’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 

Ne pas supprimer les réponses correctes déjà fournies, mais vous pouvez en fait utiliser des contraintes de type dans la correspondance de modèle. La syntaxe est:

 | :? type -> 

Ou si vous souhaitez combiner la vérification de type et la diffusion:

 | :? type as foo ->