đ CE - Fondements thĂ©oriques
CE = Computation Expression
CE : le couteau suisse âš
Les computation expressions servent à différentes choses :
CâŻ
yield return
â FâŻseq {}
CâŻ
async/await
â FâŻasync {}
C⯠expressions LINQ
from... select
â FâŻquery {}
...
Fondements théoriques sous-jacents :
MonoĂŻde
Monade
Applicative
MonoĂŻde
â Type T
définissant un ensemble comportant :
Opération
(+) : T -> T -> T
Pour combiner des ensembles et garder le mĂȘme "type"
Associative :
a + (b + c)
âĄ(a + b) + c
ĂlĂ©ment neutre (aka identity) â ensemble vide
Combinable Ă tout ensemble sans effet
a + e
âĄe + a
âĄa
CE monoĂŻdale
Le builder d'une CE monoĂŻdale (telle que seq
) dispose a minima de :
Yield
pour construire l'ensemble élément par élémentCombine
âĄ(+)
(Seq.append
)Zero
⥠élément neutre (Seq.empty
)
S'y ajoute généralement (entre autres) :
For
pour supporterfor x in xs do ...
YieldFrom
pour supporteryield!
Monade
â Type gĂ©nĂ©rique M<'T>
comportant :
Fonction
return
de constructionSignature :
(value: 'T) -> M<'T>
â Wrap une valeur
Fonction
bind
de "liaison" (aka opérateur>>=
)Signature :
(f: 'T -> M<'U>) -> M<'T> -> M<'U>
Utilise la valeur wrappée, la "map" avec la fonction
f
vers une valeur d'un autre type et "re-wrap" le résultat
Lois
return
⥠élément neutre pour bind
Ă gauche :
return x |> bind f
âĄf x
Ă droite :
m |> bind return
âĄm
bind
est associatif
m |> bind f |> bind g
âĄm |> bind (fun x -> f x |> bind g)
Langages
Haskell
Monades beaucoup utilisées. Les + communes :
IO
,Maybe
,State
,Reader
.Monad
est une classe de type pour créer facilement ses propres monades.
FâŻ
Certaines CE permettent des opérations monadiques.
Plus rarement utilisées directement (sauf par des Haskellers)
CâŻ
Monade implicite dans LINQ
Librairie LanguageExt de programmation fonctionnelle
CE monadique
Le builder d'une CE monadique dispose des méthodes Return
et Bind
.
Les types Option
et Result
sont monadiques.
â On peut leur crĂ©er leur propre CE :
type OptionBuilder() =
member _.Bind(x, f) = x |> Option.bind f
member _.Return(x) = Some x
type ResultBuilder() =
member _.Bind(x, f) = x |> Result.bind f
member _.Return(x) = Ok x
CE monadique et générique
FSharpPlus propose une CE monad
â Marche pour tous les types monadiques : Option
, Result
, ... et mĂȘme Lazy
!
#r "nuget: FSharpPlus"
open FSharpPlus
let lazyValue = monad {
let! a = lazy (printfn "I'm lazy"; 2)
let! b = lazy (printfn "I'm lazy too"; 10)
return a + b
} // System.Lazy<int>
let result = lazyValue.Value
// I'm lazy
// I'm lazy too
// val result : int = 12
Exemple avec le type Option
Option
#r "nuget: FSharpPlus"
open FSharpPlus
let addOptions x' y' = monad {
let! x = x'
let! y = y'
return x + y
}
let v1 = addOptions (Some 1) (Some 2) // Some 3
let v2 = addOptions (Some 1) None // None
#r "nuget: FSharpPlus"
open FSharpPlus
let v1 = monad {
let! a = Ok 2
let! b = Some 10
return a + b
} // đ„ Error FS0043...
let v2 = monad {
let! a = Ok 2
let! b = Some 10 |> Option.toResult
return a + b
} // val v2 : Result<int,unit> = Ok 12
CE monadiques spécifiques
Librairie FsToolkit.ErrorHandling propose :
âą CE option {}
spécifique au type Option<'T>
(exemple ci-dessous)
âą CE result {}
spécifique au type Result<'Ok, 'Err>
â RecommandĂ© car + explicite que CE monad
#r "nuget: FSToolkit.ErrorHandling"
open FsToolkit.ErrorHandling
let addOptions x' y' = option {
let! x = x'
let! y = y'
return x + y
}
let v1 = addOptions (Some 1) (Some 2) // Some 3
let v2 = addOptions (Some 1) None // None
Applicative
A.k.a Applicative Functor
â Type gĂ©nĂ©rique M<'T>
3 "styles" :
Style A : Applicatives avec apply
/<*>
et pure
/return
âą â Pas facile Ă comprendre
âą đĄ PrĂ©sentĂ© par JĂ©rĂ©mie Chassaing dans le talk âApplicatives in real lifeâ
âą â DĂ©conseillĂ© par Don Syme dans cette note de nov. 2020
Style B : Applicatives avec mapN
âą map2
, map3
... map5
combine 2 à 5 valeurs wrappées
Style C : Applicatives avec let! ... and! ...
dans une CE
âą MĂȘme principe : combiner plusieurs valeurs wrappĂ©es
⹠Disponible à partir de F⯠5 (annonce de nov. 2020)
â Conseil : Styles B et C sont autant recommandĂ©s l'un que l'autre.
CE applicative
Librairie FsToolkit.ErrorHandling propose :
âą Type Validation<'Ok, 'Err>
⥠Result<'Ok, 'Err list>
âą CE validation {}
supportant syntaxe let!...and!...
Permet d'accumuler les erreurs â Usages : âą Parsing d'inputs externes âą Smart constructor (Exemple de code slide suivante...)
#r "nuget: FSToolkit.ErrorHandling"
open FsToolkit.ErrorHandling
type [<Measure>] cm
type Customer = { Name: string; Height: int<cm> }
let validateHeight height =
if height <= 0<cm>
then Error "Height must me positive"
else Ok height
let validateName name =
if System.String.IsNullOrWhiteSpace name
then Error "Name can't be empty"
else Ok name
module Customer =
let tryCreate name height : Result<Customer, string list> =
validation {
let! validName = validateName name
and! validHeight = validateHeight height
return { Name = validName; Height = validHeight }
}
let c1 = Customer.tryCreate "Bob" 180<cm> // Ok { Name = "Bob"; Height = 180 }
let c2 = Customer.tryCreate "Bob" 0<cm> // Error ["Height must me positive"]
let c3 = Customer.tryCreate "" 0<cm> // Error ["Name can't be empty"; "Height must me positive"]
Applicative vs Monade
Soit N opérations
tryXxx
renvoyant unOption
ouResult
Style monadique :
Avec
bind
ou CElet! ... let! ...
Chaßne les opérations, exécutée 1 à 1, la N dépendant de la N-1
S'arrĂȘte Ă 1Ăšre opĂ©ration KO â juste 1Ăšre erreur dans
Result
âRailway-oriented programming de Scott Wlaschin
module Result =
// f : 'T -> Result<'U, 'Err>
// x': Result<'T, 'Err>
// -> Result<'U, 'Err>
let bind f x' =
match x' with
| Error e -> Error e // đ (1)
| Ok value -> f value
Style applicatif :
Avec
mapN
ou CElet! ... and! ...
Combine 2..N opĂ©rations indĂ©pendantes â parallĂ©lisables đ
Permet de combiner les cas
Error
contenant uneList
âĄ
module Validation =
// f : 'T -> 'U -> Result<'V, 'Err list>
// x': Result<'T, 'Err list>
// y': Result<'U, 'Err list>
// -> Result<'V, 'Err list>
let map2 f x' y' =
match x', y' with
| Ok x, Ok y -> f x y
| Ok _, Error errors | Error errors, Ok _ -> Error errors
| Error errors1, Error errors2 -> Error (errors1 @ errors2) // đ âĄ
Autres CE
On a vu 2 librairies qui étendent F⯠et proposent leurs CE :
FSharpPlus â
monad
FsToolkit.ErrorHandling â
option
,result
,validation
Beaucoup de librairies ont leur propre DSL (Domain Specific Language.) Certaines s'appuient alors sur des CE :
Expecto
Farmer
Saturn
Expecto
â Librairie de testing : assertions + runner â đ https://github.com/haf/expecto
open Expecto
let tests =
test "A simple test" {
let subject = "Hello World"
Expect.equal subject "Hello World" "The strings should equal"
}
[<EntryPoint>]
let main args =
runTestsWithCLIArgs [] args tests
Farmer
â Infrastructure-as-code pour Azure â
đ github.com/compositionalit/farmer
// Create a storage account with a container
let myStorageAccount = storageAccount {
name "myTestStorage"
add_public_container "myContainer"
}
// Create a web app with application insights that's connected to the storage account
let myWebApp = webApp {
name "myTestWebApp"
setting "storageKey" myStorageAccount.Key
}
// Create an ARM template (Azure Resource Manager)
let deployment = arm {
location Location.NorthEurope
add_resources [
myStorageAccount
myWebApp
]
}
// Deploy it to Azure!
deployment
|> Writer.quickDeploy "myResourceGroup" Deploy.NoParameters
Saturn
â Framework Web au-dessus de ASP.NET Core, pattern MVC â
đ saturnframework.org
open Saturn
open Giraffe
let app = application {
use_router (text "Hello World from Saturn")
}
run app
Aller + loin
đč Extending F# through Computation Expressions - đ Slides
Last updated
Was this helpful?