Recherche…
Remarques
Unités au runtime
Les unités de mesure sont utilisées uniquement pour la vérification statique par le compilateur et ne sont pas disponibles au moment de l'exécution. Ils ne peuvent pas être utilisés en réflexion ou dans des méthodes comme ToString
.
Par exemple, C # donne un double
sans unités pour un champ de type float<m>
défini et exposé depuis une bibliothèque F #.
Assurer la cohérence des unités dans les calculs
Les unités de mesure sont des annotations de type supplémentaires pouvant être ajoutées à des flottants ou à des nombres entiers. Ils peuvent être utilisés pour vérifier lors de la compilation que les calculs utilisent des unités de manière cohérente.
Pour définir des annotations:
[<Measure>] type m // meters
[<Measure>] type s // seconds
[<Measure>] type accel = m/s^2 // acceleration defined as meters per second squared
Une fois définies, les annotations peuvent être utilisées pour vérifier qu'une expression génère le type attendu.
// Compile-time checking that this function will return meters, since (m/s^2) * (s^2) -> m
// Therefore we know units were used properly in the calculation.
let freeFallDistance (time:float<s>) : float<m> =
0.5 * 9.8<accel> * (time*time)
// It is also made explicit at the call site, so we know that the parameter passed should be in seconds
let dist:float<m> = freeFallDistance 3.0<s>
printfn "%f" dist
Conversions entre unités
[<Measure>] type m // meters
[<Measure>] type cm // centimeters
// Conversion factor
let cmInM = 100<cm/m>
let distanceInM = 1<m>
let distanceInCM = distanceInM * cmInM // 100<cm>
// Conversion function
let cmToM (x : int<cm>) = x / 100<cm/m>
let mToCm (x : int<m>) = x * 100<cm/m>
cmToM 100<cm> // 1<m>
mToCm 1<m> // 100<cm>
Notez que le compilateur F # ne sait pas que 1<m>
est égal à 100<cm>
. En ce qui concerne les soins, les unités sont des types distincts. Vous pouvez écrire des fonctions similaires pour convertir des mètres en kilogrammes et le compilateur ne s'en souciera pas.
[<Measure>] type kg
// Valid code, invalid physics
let kgToM x = x / 100<kg/m>
Il n'est pas possible de définir des unités de mesure sous la forme de multiples d'autres unités telles que
// Invalid code
[<Measure>] type m = 100<cm>
Cependant, il est très simple de définir des unités "par quelque chose", par exemple Hertz, la fréquence de mesure est simplement "par seconde".
// Valid code
[<Measure>] type s
[<Measure>] type Hz = /s
1 / 1<s> = 1 <Hz> // Evaluates to true
[<Measure>] type N = kg m/s // Newtons, measuring force. Note lack of multiplication sign.
// Usage
let mass = 1<kg>
let distance = 1<m>
let time = 1<s>
let force = mass * distance / time // Evaluates to 1<kg m/s>
force = 1<N> // Evaluates to true
Utiliser LanguagePrimitives pour préserver ou définir des unités
Lorsqu'une fonction ne préserve pas automatiquement les unités en raison d'opérations de niveau inférieur, le module LanguagePrimitives
peut être utilisé pour définir des unités sur les primitives qui les prennent en charge:
/// This cast preserves units, while changing the underlying type
let inline castDoubleToSingle (x : float<'u>) : float32<'u> =
LanguagePrimitives.Float32WithMeasure (float32 x)
Pour affecter des unités de mesure à une valeur à virgule flottante double précision, multipliez simplement par une unité avec les unités correctes:
[<Measure>]
type USD
let toMoneyImprecise (amount : float) =
amount * 1.<USD>
Pour affecter des unités de mesure à une valeur sans unité qui n'est pas System.Double, par exemple, provenant d'une bibliothèque écrite dans une autre langue, utilisez une conversion:
open LanguagePrimitives
let toMoney amount =
amount |> DecimalWithMeasure<'u>
Voici les types de fonctions signalés par F # interactive:
val toMoney : amount:decimal -> decimal<'u>
val toMoneyImprecise : amount:float -> float<USD>
Paramètres de type d'unité de mesure
L'attribut [<Measure>]
peut être utilisé sur les paramètres de type pour déclarer les types génériques par rapport aux unités de mesure:
type CylinderSize<[<Measure>] 'u> =
{ Radius : float<'u>
Height : float<'u> }
Test d'utilisation:
open Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols
/// This has type CylinderSize<m>.
let testCylinder =
{ Radius = 14.<m>
Height = 1.<m> }
Utiliser des types d'unité standardisés pour maintenir la compatibilité
Par exemple, les types pour les unités SI ont été normalisés dans la bibliothèque principale F #, dans Microsoft.FSharp.Data.UnitSystems.SI
. Ouvrez le sous-espace de noms approprié, UnitNames
ou UnitSymbols
, pour les utiliser. Ou, si seulement quelques unités SI sont requises, elles peuvent être importées avec des alias de type:
/// Seconds, the SI unit of time. Type abbreviation for the Microsoft standardized type.
type [<Measure>] s = Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols.s
Certains utilisateurs ont tendance à faire ce qui suit, ce qui ne devrait pas être fait chaque fois qu'une définition est déjà disponible:
/// Seconds, the SI unit of time
type [<Measure>] s // DO NOT DO THIS! THIS IS AN EXAMPLE TO EXPLAIN A PROBLEM.
La différence devient évidente lors de l’interfaçage avec un autre code faisant référence aux types de SI standard. Le code faisant référence aux unités standard est compatible, tandis que le code qui définit son propre type est incompatible avec tout code n'utilisant pas sa définition spécifique.
Par conséquent, utilisez toujours les types standard pour les unités SI. Peu importe que vous fassiez référence à UnitNames
ou UnitSymbols
, puisque les noms équivalents dans ces deux UnitSymbols
font référence au même type:
open Microsoft.FSharp.Data.UnitSystems.SI
/// This is valid, since both versions refer to the same authoritative type.
let validSubtraction = 1.<UnitSymbols.s> - 0.5<UnitNames.second>