2015-02-28 2 views
1

Lors de l'écriture de fonctions génériques dans F #, je peux utiliser des membres définis dans le module LanguagePrimitives, comme par ex. dans cette fonction, cela incrémente simplement un nombreUnités génériques dans F #

let inline increment (x : 'a) = 
    x + LanguagePrimitives.GenericOne 

Je me demande s'il y a quelque chose de similaire à travailler avec des unités de mesure. En particulier: est-il possible d'écrire une fonction qui prend un argument générique et le convertit en un nombre du même type avec une unité. Quelque chose comme:

let inline toNumberWithUnit<[<Measure>] 'u> x = 
    x * GenericOne<'u> //that won't work 

Cela aurait un type: 'a ->' a < « u>. C'est possible ?

Répondre

2

Cette signature serait illégale dans .NET, de sorte que le seul moyen sera d'utiliser la fonction F # en ligne avec des contraintes statiques.

Vous pouvez alors définir une fonction comme ceci:

[<Measure>] 
type M = class end 

let x = LanguagePrimitives.FloatWithMeasure<M> 2. 

type T<[<Measure>]'M>() = 
    static member ($) (T, x) = LanguagePrimitives.FloatWithMeasure<'M> x 
    static member ($) (T, x) = LanguagePrimitives.Float32WithMeasure<'M> x 
    static member ($) (T, x) = LanguagePrimitives.Int32WithMeasure<'M> x 
    // more overloads 

let inline NumberWithMeasure x = T() $ x 

let a: float<M> = NumberWithMeasure 2. 
let b: float32<M> = NumberWithMeasure 2.0f 
let c: int<M>  = NumberWithMeasure 2 

Le principal problème lorsqu'ils traitent avec des chiffres génériques et les unités de mesure que vous vous retrouvez avec les signatures où vous un avez un type générique avec un type paramètre (un higher kind) qui pour le moment ne sont pas pris en charge dans .NET.

MISE À JOUR

Après un certain temps, je courais aussi bien dans cette situation et a trouvé cette réponse qui arrive à venir de moi :)

Après essayer avec différentes unités de mesures que je compris qu'il n » t travail, car l'inférence de type ne généralise pas sur les unités de mesure, l'exemple affiché fonctionne parce que l'inférence de type témoigne de l'utilisation de la mesure M, puis spécialise la fonction sur M.

Mais est ici un moyen de le faire fonctionner, en utilisant explicitement l'opérateur $ défini ci-dessus:

[<Measure>] type km 
[<Measure>] type miles 

type WithMeasure<[<Measure>]'M>() = 
    static member ($) (x, T) = LanguagePrimitives.FloatWithMeasure<'M> x 
    static member ($) (x, T) = LanguagePrimitives.Float32WithMeasure<'M> x 
    static member ($) (x, T) = LanguagePrimitives.Int32WithMeasure<'M> x 
    static member ($) (x, T) = LanguagePrimitives.DecimalWithMeasure<'M> x 
    static member ($) (x, T) = LanguagePrimitives.Int16WithMeasure<'M> x 
    static member ($) (x, T) = LanguagePrimitives.Int64WithMeasure<'M> x 
    static member ($) (x, T) = LanguagePrimitives.SByteWithMeasure<'M> x 
    // no more overloads 


let a: float<km>  = 2. $WithMeasure() 
let b: float32<miles> = 2.0f $WithMeasure() 

Il pourrait y avoir un moyen de créer une fonction générique, ou une constante générique, mais pour le moment il semble ne pas être possible avec la version actuelle de F #.

Je vais essayer avec F # 4.1 quand il est prêt.

+0

Donc je ne peux pas avoir une fonction générique, mais plutôt une séquence de fonctions et je dois préciser laquelle je veux utiliser. Bien - pas encore ce que j'aimerais avoir, mais comme tu dis que c'est le meilleur que je peux actuellement obtenir, ça doit être suffisant. Merci beaucoup, monsieur :). –