Formation F#
  • Intro
  • Bases
    • Le F♯, c'est quoi ?
    • Syntaxe
    • Premiers concepts
    • 🍔 Quiz
  • Fonctions
    • Signature
    • Fonctions
    • Fonctions standard
    • Opérateurs
    • Fonctions : compléments
    • 🍔 Quiz
    • 📜 Récap’
  • Types composites
    • Généralités
    • Tuples
    • Records
    • Unions
    • Enums
    • Records anonymes
    • Types valeur
    • 🍔 Quiz
  • Types : Compléments
    • Type unit
    • Génériques
    • Types flexibles
    • Unités de mesure
    • Conversion
    • Exceptions F#
  • Pattern matching
    • Patterns
    • Match expression
    • 🚀 Active Patterns
    • 📜 Récap’
  • Collections
    • Vue d'ensemble
    • Types
    • Fonctions génériques
    • Fonctions spécifiques
    • 🍔 Quiz
    • 📜 Récap’
  • Programmation asynchrone
    • Workflow asynchrone
    • Interop avec la TPL .NET
    • 📜 Récap’
  • Types monadiques
    • Type Option
    • Type Result
    • Smart constructor
    • 🚀 Computation expression (CE)
    • 🚀 CE - Fondements théoriques
    • 📜 Récap’
  • Module & namespace
    • Vue d'ensemble
    • Namespace
    • Module
    • 🍔 Quiz
    • 📜 Récap’
  • Orienté-objet
    • Introduction
    • Membres
    • Extensions de type
    • Classe, structure
    • Interface
    • Expression objet
    • Recommandations
  • 🦚 Aller plus loin
Propulsé par GitBook
Sur cette page
  • Présentation
  • Déclaration
  • SI
  • Usage
  • Symbole
  • Conversion
  • Ajouter/supprimer
  • Effacées au runtime !
  • Type avec unité générique
  • Unité pour primitive non numérique

Cet article vous a-t-il été utile ?

Modifier sur GitHub
  1. Types : Compléments

Unités de mesure

Présentation

Moyen d'associer un type numérique à une unité de mesure

  • Durée : s aka second

  • Masse : kg

  • Longueur : m aka metre

  • ...

Les unités sont vérifiées à la compilation

  • Empêche d'ajouter des 🥦 à des 🥕 → code + sûr

  • Permet de les combiner : Vitesse = Distance / Durée → m/s

Déclaration

Syntaxe basée sur attribut [<Measure>]

// 👉 Nouvelles unités "from scratch"
[<Measure>] type kilogram
[<Measure>] type metre
[<Measure>] type second

// 👉 Alias d'unités existantes
[<Measure>] type kg = kilogram
[<Measure>] type m  = metre
[<Measure>] type s  = second

// 👉 Combinaison d'unités existantes
[<Measure>] type Hz = / s
[<Measure>] type N = kg m / s^2

SI

Les unités du Système International sont prédéfinies dans les namespaces :

FSharp.Data.UnitSystems.SI.UnitNames :

  • ampere, hertz, joule, kelvin, kilogram, metre...

  • https://fsharp.github.io/fsharp-core-docs/reference/fsharp-data-unitsystems-si-unitnames.html

FSharp.Data.UnitSystems.SI.UnitSymbols

  • A, Hz, J, K, kg, m...

  • https://fsharp.github.io/fsharp-core-docs/reference/fsharp-data-unitsystems-si-unitsymbols.html

Usage

// Unité définie en annotant le nombre
let distance = 1.0<m>               // val distance : float<m> = 1.0
let time = 2.0<s>                   // val time : float<s> = 2.0

// Unité combinée, inférée
let speed = distance / time         // val speed : float<m/s> = 0.5

// Unité combinée, définie par annotation
let [<Literal>] G = 9.806<m/s^2>    // val G : float<m/s ^ 2> = 9.806

// Comparaison
let sameFrequency = (1<Hz> = 1</s>)   // ✅ true
let ko1 = (distance = 1.0)            // ❌ Error FS0001: Incompatibilité de type.
                                      // 💥 Attente de 'float<m>' mais obtention de 'float'
let ko2 = (distance = 1<m>)           // 💥 Attente de 'float<m>' mais obtention de 'int<m>'
let ko3 = (distance = time)           // 💥 Attente de 'float<m>' mais obtention de 'float<s>'

Symbole

💡 Astuce : utilisation des doubles back ticks

[<Measure>] type ``Ω``
[<Measure>] type ``°C``
[<Measure>] type ``°F``

let waterFreezingAt = 0.0<``°C``>
// val waterFreezingAt : float<°C> = 0.0

let waterBoilingAt = 100.0<``°C``>
// val waterBoilingAt : float<°C> = 100.0

Conversion

  • Facteur multiplicatif avec une unité <target/source>

  • Fonction de conversion utilisant ce facteur

[<Measure>] type m
[<Measure>] type cm
[<Measure>] type km

module Distance =
    let toCentimeter (x: float<m>) = // (x: float<m>) -> float<cm>
        x * 100.0<cm/m>

    let toKilometer (x: float<m>) = // (x: float<m>) -> float<km>
        x / 1000.0<m/km>

let a = Distance.toCentimeter 1.0<m>   // val a : float<cm> = 100.0
let b = Distance.toKilometer 500.0<m>  // val b : float<km> = 0.5

Exemple 2 : degré Celsius (°C) → degré Fahrenheit (°F)

[<Measure>] type ``°C``
[<Measure>] type ``°F``

module Temperature =
    let toFahrenheit ( x: float<``°C``> ) =  // (x: float<°C>) -> float<°F>
        9.0<``°F``> / 5.0<``°C``> * x + 32.0<``°F``>

let waterFreezingAt = Temperature.toFahrenheit 0.0<``°C``>
// val waterFreezingAt : float<°F> = 32.0

let waterBoilingAt = Temperature.toFahrenheit 100.0<``°C``>
// val waterBoilingAt : float<°F> = 212.0

Ajouter/supprimer

Ajouter une unité à un nombre nu :

  • ✅ number * 1.0<target>

Supprimer l'unité d'un nombre number : float<source> :

  • ✅ number / 1.0<source>

  • ✅ float number

Créer une liste de nombres avec unité :

  • ✅ [1<m>; 2<m>; 3<m>]

  • ❌ [1<m>..3<m>] (un range nécessite des nombres nus)

  • ✅ [ for i in [1..3] -> i * 1<m> ]

Effacées au runtime !

Les unités de mesure sont propres au compilateur F♯. → Elles ne sont pas compilées en .NET

Type avec unité générique

Besoin de distinguer d'un type générique classique → Annoter l'unité générique avec [<Measure>]

type Point<[<Measure>] 'u, 'data> =
    { X: float<'u>; Y: float<'u>; Data: 'data }

let point = { X = 10.0<m>; Y = 2.0<m>; Data = "abc" }
// val point : Point<m, string> = { X = 10.0; Y = 2.0; Data = "abc" }

Unité pour primitive non numérique

open System

#r "nuget: FSharp.UMX"
open FSharp.UMX

[<Measure>] type ClientId
[<Measure>] type OrderId

type Order = { Id: Guid<OrderId>; ClientId: string<ClientId> }

let order = { Id = % Guid.NewGuid(); ClientId = % "RDE" }
PrécédentTypes flexiblesSuivantConversion

Dernière mise à jour il y a 2 ans

Cet article vous a-t-il été utile ?

💡 Nuget (Unit of Measure Extension) → Pour autres primitives bool, DateTime, Guid, string, TimeSpan

FSharp.UMX