Question

J'essaie de comprendre comment définir une fonction qui fonctionne sur plusieurs types de paramètres (par exemple, int et int64). Si je comprends bien, la surcharge de fonctions n’est pas possible en F # (le compilateur se 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 modèle ne sont apparemment pas prises en charge), bien que cela illustre ce que j'aimerais réaliser: une fonction qui opère sur plusieurs types de paramètres et renvoie une valeur du type correspondant. J'ai l'impression que c'est possible dans F # en utilisant une combinaison de types génériques / inférence de type / correspondance de motif, mais la syntaxe m'a échappé. J'ai aussi essayé d'utiliser le:? opérateur (tests de type dynamique) et lorsque dans le bloc de correspondance de modèle, mais cela produit toujours des erreurs de tri.

Comme je suis plutôt nouveau dans la langue, je peux très bien essayer de faire quelque chose d’impossible ici, alors faites-le-moi savoir, s’il existe une autre solution.

Était-ce utile?

La solution

La surcharge est typiquement le bogue des langages à inférence de types (du moins lorsque, comme F #, le système de types n’est pas assez puissant pour contenir des classes de types). Vous avez plusieurs choix en fa #:

  • Utilisez la surcharge sur les méthodes (membres d'un type), auquel cas la surcharge fonctionne comme dans les autres langages .Net (vous pouvez utiliser des membres de surcharge ad-hoc, à condition que les appels puissent être distingués par le nombre / type de paramètres)
  • Utilisez & "inline &"; & "; ^ &"; et les contraintes de membre statique pour la surcharge ad-hoc sur les fonctions (c’est ce que la plupart des opérateurs mathématiques besoin de travailler sur int / float / etc; la syntaxe ici est bizarre, elle est peu utilisée en dehors de la bibliothèque F #)
  • Simulez les classes de types en passant un paramètre de dictionnaire d'opérations supplémentaire (c'est ce que fait INumeric dans l'une des bibliothèques F # PowerPack pour généraliser divers algorithmes Math pour des types arbitraires définis par l'utilisateur)
  • Revenir au typage dynamique (passez un paramètre 'obj', effectuez un test de type dynamique, lève une exception d'exécution pour un type incorrect)

Pour votre exemple particulier, j'utiliserais probablement juste 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

Autres conseils

Ceci fonctionne:

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 permet une recherche à la compilation du type de l'argument.

Les contraintes statiques sont générées automatiquement en présence d'un opérateur (opérateur $ dans ce cas) mais peuvent toujours être écrites à 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)

En savoir plus sur cette ici .

Oui, cela peut être fait. Consultez ce fil de discussion 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

Avertissement : aucune vérification de type à la compilation. C'est à dire. sqrt_int "blabla" compile bien mais vous obtiendrez une exception FormatException lors de l'exécution.

Voici un autre moyen d'utiliser les contrôles 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 enlever les bonnes réponses déjà fournies, mais vous pouvez en fait utiliser des contraintes de type dans la recherche de modèle. La syntaxe est la suivante:

| :? type ->

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

| :? type as foo ->
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top