Programmation fonctionnelle en Swift : niveau avancé

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Programmation fonctionnelle en Swift : niveau avancé



Swift a été conçu dès le départ comme un langage multi-paradigme, ce qui en fait une plateforme idéale pour appliquer des techniques avancées de programmation fonctionnelle. Dans cet article, nous explorerons des concepts qui vont au-delà des fermetures de base et de map/filter/reduce — nous parlerons des foncteurs, des monades, de la composition de fonctions, du curryfication et de la mémorisation. Ces outils vous aideront à écrire un code plus prévisible, testable et expressif pour les applications iOS et macOS.



Foncteurs et monades : travailler avec des contextes



Un foncteur est un type qui implémente la fonction map, permettant d'appliquer une transformation à une valeur à l'intérieur d'un contexte. En Swift, les exemples classiques de foncteurs sont Optional et Array.



// Foncteur Optionallet value: Int? = 42let result = value.map { $0 * 2 } // Optional(84)

// Foncteur Arraylet numbers = [1, 2, 3]let doubled = numbers.map { $0 * 2 } // [2, 4, 6]


Une monade est un foncteur avec une opération supplémentaire flatMap (ou >>= en Haskell). Elle permet de "déballer" des contextes imbriqués. Swift prend en charge les monades via le protocole Monad de manière implicite, mais les types clés (Optional, Result, Async) implémentent cette logique.



// Monade Optional : chaîne d'opérations avec nil possiblefunc parseNumber(_ string: String) -> Int? { return Int(string) }func half(_ number: Int) -> Int? { return number % 2 == 0 ? number / 2 : nil }

let input = "42"let result = input.flatMap(parseNumber).flatMap(half) // Optional(21)

// Monade Result pour la gestion des erreursenum AppError: Error { case invalidInput }func safeDivide(_ a: Int, _ b: Int) -> Result<Int, AppError> { guard b != 0 else { return .failure(.invalidInput) } return .success(a / b)}

let divisionResult = safeDivide(10, 2).flatMap { safeDivide($0, 1) } // .success(5)


Application avancée : création d'un type monadique personnalisé pour travailler avec des opérations asynchrones.



struct Async<T> {    let run: (@escaping (T) -> Void) -> Void        func map<U>(transform: @escaping (T) -> U) -> Async<U> {        return Async<U> { callback in            self.run { value in callback(transform(value)) }        }    }        func flatMap<U>(transform: @escaping (T) -> Async<U>) -> Async<U> {        return Async<U> { callback in            self.run { value in transform(value).run(callback) }        }    }}


Composition de fonctions : construire un pipeline



La composition de fonctions consiste à créer une nouvelle fonction en appliquant séquentiellement plusieurs fonctions. En Swift, cela peut être implémenté via un opérateur ou des fermetures.



// Opérateur de compositionprecedencegroup CompositionPrecedence {    associativity: left    higherThan: FunctionArrowPrecedence}

infix operator >>> : CompositionPrecedence

func >>>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C { return { x in g(f(x)) }}

// Exemple : validation et formatagelet trimWhitespace: (String) -> String = { $0.trimmingCharacters(in: .whitespaces) }let capitalize: (String) -> String = { $0.capitalized }let addExclamation: (String) -> String = { $0 + "!" }

let processString = trimWhitespace >>> capitalize >>> addExclamationprint(processString(" hello world ")) // "Hello World!"


La composition est particulièrement utile pour travailler avec des chaînes de transformations de données, par exemple lors du traitement de réponses réseau.



// Composition avancée avec gestion des erreurstypealias ResultFunction = (T) -> Result<U, Error>

func compose(_ f: @escaping ResultFunction<T, U>, _ g: @escaping ResultFunction<U, V>) -> ResultFunction<T, V> { return { input in

Blogs

Book Recommendations