Fonctions : compléments

Mémoïsation

💡 Idée : réduire le temps de calcul d'une fonction

Comment : mise en cache des résultats → Au prochain appel avec mêmes arguments, renverra résultat en cache

👉 En pratique : fonction memoizeN de la librairie FSharpPlus

⚠️ Attention : Comme tout optimisation, à utiliser quand le besoin se fait sentir et en validant (mesurant) que cela marche sans désagrément annexe.

☝ Ne pas confondre avec expression lazy (slide suivante)

Lazy expression

Sucre syntaxique pour créer un objet .NET Lazy<'T> à partir d'une expression → Expression pas évaluée immédiatement mais qu'à la 1ère demande (Thunk) → Intéressant pour améliorer performance sans trop complexifier le code

let printAndForward x = printfn $"{x}"; x

let a = lazy (printAndForward "a")

let b = printAndForward "b"
// > b

printfn $"{a.Value} et {b}"
// > a
// > a et b

printfn $"{a.Value} et c"
// > a et c

Lazy active pattern

Extrait la valeur dans un objet Lazy

Organisation des fonctions

3 façons d'organiser les fonctions = 3 endroits où les déclarer :

  • Module : fonction déclarée dans un module 📍 Module

  • Nested : fonction déclarée à l'intérieur d'une valeur / fonction

    • 💡 Encapsuler des helpers utilisés juste localement

    • ☝ Paramètres de la fonction chapeau accessibles à fonction nested

  • Method : fonction définie comme méthode dans un type (next slide)

Méthodes

  • Définies avec mot-clé member plutôt que let

  • Choix du self-identifier : this, me, _...

  • Paramètres sont au choix :

    • Tuplifiés : style OOP

    • Curryfiés : style FP

Méthodes - Exemple

Fonction vs Méthode

Fonctionnalité
Fonction
Méthode

Nommage

camelCase

PascalCase

Curryfication

✅ oui

✅ si ni tuplifiée, ni surchargée

Paramètres nommés

❌ non

✅ si tuplifiés

Paramètres optionnels

❌ non

✅ si tuplifiés

Surcharge / overload

❌ non

✅ si tuplifiés

Inférence des arguments → à la déclaration

➖ Possible

✅ oui, du this ➖ Possible pour les autres arguments

→ à l'usage

✅ oui

❌ non, il faut annoter le type de l'objet pour utiliser une de ses méthodes

En argument d'une high-order fn

✅ oui

❌ non, lambda nécessaire

Support du inline

✅ oui

✅ oui

Récursive

✅ si rec

✅ oui

How To: Pipeline avec une méthode d'instance ?

Exemple : comment "piper" la méthode ToLower() d'une string ?

  • Via lambda : "MyString" |> (fun x -> x.ToLower())

  • Idem via fonction nommée telle que :

    • String.toLower de la librairie FSharpPlus

    • "MyString" |> String.toLower

Alternative au pipeline : passer par une valeur intermédiaire : → let low = "MyString".ToLower()

Interop avec la BCL

BCL = Base Class Library .NET

Méthode void

Une méthode .NET void est vue en F# comme renvoyant unit.

Réciproquement, une fonction F# renvoyant unit est compilée en une méthode void.

Equivalent C# d'après SharpLab :

Appel à une méthode de la BCL à N arguments

Une méthode .NET avec plusieurs arguments est "pseudo-tuplifiée" :

  • Les arguments doivent tous être spécifiés (1)

  • L'application partielle des paramètres n'est pas supportée (2)

  • Les appels ne marche pas avec un vrai tuple F♯ ⚠️ (3)

💡 Note : on peut utiliser l'opérateur pipe |> pour appeler une méthode tuplifiée → Cf. Orienté-objet / Membres 📍

Paramètre out - En C♯

out utilisé pour avoir plusieurs valeurs en sortie → Ex : Int32.TryParse, Dictionary<,>.TryGetValue :

Paramètre out - En F♯

Possibilité de consommer la sortie sous forme de tuple 👍

💡 Fonctions F♯ tryXxx s'appuient plutôt sur le type Option<T> 📍

Instancier une classe avec new ?

Appel d'une méthode surchargée

  • Compilateur peut ne pas comprendre quelle surcharge est appelée

  • Astuce : faire appel avec argument nommé

Mis à jour

Ce contenu vous a-t-il été utile ?