Vous êtes sur la page 1sur 14

La programmation fonctionnelle

Par Jerme Valloire


Date de publication : 9 juin 2015

Les confrences d'Hadi Hariri, dveloppeur et vangliste technologique chez JetBrains,


sont pour moi des confrences ne pas manquer Devoxx France, non seulement du
fait de ses qualits indniables d'orateur, mais aussi de la diversit et de la pertinence des
sujets traits.
Cette anne, Hadi Hariri a dcid de s'attaquer la programmation fonctionnelle au travers
d'une confrence intitule Refactoring to Functional au cours de laquelle il est revenu sur
les avantages de ce paradigme de programmation en s'appuyant sur diffrents exemples
concrets.
Vous pouvez donner votre avis sur cet article sur notre forum : Commentez

La programmation fonctionnelle par Jerme Valloire

I - Introduction : pourquoi se tourner vers la programmation fonctionnelle ?..............................................................3


II - Quels langages utiliser ?........................................................................................................................................3
III - L'approche fonctionnelle en action....................................................................................................................... 4
III-A - Une premire fonction : itDoesSomething................................................................................................4
III-B - Un exemple de refactoring tape par tape................................................................................................5
IV - Et les patterns Object-Oriented dans tout a ?....................................................................................................6
IV-A - Patron de mthode (Template Method Pattern)........................................................................................7
IV-B - Stratgie.......................................................................................................................................................7
IV-C - Injection de dpendances........................................................................................................................... 8
V - Apprendre rflchir fonctionnel .................................................................................................................... 9
V-A - Des fonctions comme primitives rutilisables du langage............................................................................9
V-B - Des donnes la recherche de l'immutabilit........................................................................................... 10
VI - Programmation fonctionnelle et performances...................................................................................................11
VI-A - Mmozation...............................................................................................................................................11
VI-B - Rcursion terminale................................................................................................................................... 12
VI-C - Fonctions inline..........................................................................................................................................13
VII - Quid de tous les concepts effrayants de la programmation fonctionnelle ?...................................................... 13
VII-A - Foncteurs (Functors).............................................................................................................................. 13
VII-B - Monades................................................................................................................................................... 13
VIII - Conclusion........................................................................................................................................................ 14
IX - Remerciements................................................................................................................................................... 14

-2-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

I - Introduction : pourquoi se tourner vers la programmation fonctionnelle ?


Pour Hadi Hariri, la programmation fonctionnelle doit permettre aux dveloppeurs :

d'crire moins de code ;


d'crire du code plus expressif ;
d'crire du code plus correct ;
d'crire du code plus performant.

De plus, et il faut bien se l'avouer, la programmation fonctionnelle est la mode aujourd'hui, avec l'avnement dans
le monde de l'entreprise de langages tels que Scala, Haskell mais aussi avec l'introduction des lambdas dans Java
depuis Java 8.
Ainsi, l'approche fonctionnelle, longtemps considre comme purement acadmique, connat aujourd'hui un essor
rel, bouleversant compltement nos habitudes de programmation imprative.
Par consquent, sa mise en uvre requiert un effort certain d'apprentissage pour tre efficacement utilise.

II - Quels langages utiliser ?


Un langage fonctionnel se caractrise par plusieurs critres :

l'utilisation de fonctions en tant qu'objets de premire classe (first-class citizens), celles-ci tant des donnes,
au mme titre que toutes autres donnes :
fonctions d'ordre suprieur (High-Order functions) : qui peuvent prendre une ou plusieurs fonctions
comme paramtres et renvoyer une nouvelle fonction comme valeur de retour,

lambda : fonctions anonymes nommes ainsi en rfrence au Lambda-calcul ;


la possibilit de manipuler des donnes immuables.

Il existe de nombreux langages fonctionnels :

Lisp, l'anctre de tous les langages fonctionnels, cr ds 1958 ;


Scheme, un driv de Lisp, cr en 1975 ;
La famille des langages ML (Meta Language), qui a vu le jour la fin des annes 70 et dont les principaux
reprsentants aujourd'hui sont SML (Standard ML) et Caml (avec OCaml) ;
Erlang, cr en 1989 ;
Haskell, cr en 1990 et qui a la particularit d'tre un langage fonctionnel pur (sans effet de bord), ce qui
en fait LE langage fonctionnel par excellence aujourd'hui.

En plus de ces langages fonctionnels historiques, on trouve galement de nombreux langages multiparadigmes, qui
permettent une approche fonctionnelle :

F#, n en 2002, qui est un langage de programmation fonctionnel, impratif et orient objet pour la plateforme .NET ;
Scala, apparu en 2003 dans le but de concilier les paradigmes de programmation oriente objet et de
programmation fonctionnelle ;
Clojure, cr en 2007 et qui est un dialecte Lisp pour la plate-forme Java ;
Java 8, mais galement les versions prcdentes, avec l'ajout de bibliothques tierces telles que Guava
ou Functional Java ;
et bien d'autres

Pour sa prsentation, Hadi Hariri a utilis Kotlin, un autre langage bas sur la JVM, qui se rapproche de Scala.
noter que ce langage est dvelopp par JetBrains, d'o son choix naturel pour cette confrence.
-3-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

1.
2.
3.
4.
5.
6.
7.
8.
9.

fun basicFunction(parameter: String): String {


return parameter
}
fun singleLineFunction(x: Int, y: Int) = x + y
fun higherOrderFunction(func: (Int) -> Int) { ... }
val lambda = { (x: Int, y: Int) -> x + y }

Pour qu'une fonction soit considre comme pure, les mmes donnes d'entre doivent toujours produire le mme
rsultat en sortie, tout en n'induisant aucun effet de bord.
Cette proprit rend possible la transparence rfrentielle, principe selon lequel le rsultat d'une opration ne
change pas si on remplace une expression par une expression de valeur gale.
Un langage fonctionnel est dit fonctionnel pur s'il est sans effet de bord. Par exemple, dans de tels langages,
on ne trouve aucune opration d'affection. C'est le cas du langage Haskell.

III - L'approche fonctionnelle en action


III-A - Une premire fonction : itDoesSomething
Hadi Hariri nous propose tout d'abord la fonction suivante :
1. fun
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14. }

itDoesSomething(elements: List<String>): Map<String, Int> {


var i = 0
val results = hashMapOf<String, Int>()
while (i < elements.size) {
val element = results.get(elements[i])
if (element != null) {
results.set(elements[i], element + 1)
} else {
results.set(elements[i], 1)
}
i++
}
return results

Comme son nom l'indique, cette fonction fait quelque chose !


Mais vous en conviendrez, il n'est pas particulirement ais de comprendre le but de cette fonction sans analyser
prcisment chacune de ses lignes de code, o s'entremlent donnes et oprations sur ces donnes.
Cependant, il y a fort parier que, si vous analysez le code des projets sur lesquels vous travaillez, ce type de code
impratif est lgion tout en esprant que les noms de ces fonctions sont quand mme un peu plus explicites !
Une version fonctionnelle de cette fonction est la suivante :
1. fun itDoesSomething(elements: List<String>): List<Pair<String, Int>> {
2.
return elements.groupBy {
3.
it
4.
}.map {
5.
Pair(it.key, it.value.count())
6.
}
7. }

-4-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

Ce code est non seulement plus concis, mais galement plus expressif et mme sans connatre les fonctions groupBy
et map, on peut commencer en deviner le but : dterminer le nombre d'occurrences de chaque chane de caractres
de la liste passe en paramtre.

III-B - Un exemple de refactoring tape par tape


Nouvel exemple, avec cette fois-ci une fonction visant filtrer une liste d'albums de Pink Floyd, le groupe prfr de
er
Hadi Hariri, pour ne conserver que les tops albums, classs 1 au Royaume-Uni et aux tats-Unis.
1. data class Album(val title: String, val year: Int, val chartUK: Int, val chartUS: Int)
2. val albums = listOf(
3.
Album("The Dark Side of the Moon", 1973, 2, 1),
4.
Album("The Wall", 1979, 3, 1),
5.
Album("Wish You Were Here", 1975, 1, 1),
6.
Album("Animals", 1977, 2, 3),
7.
Album("The Piper at the Gates of Dawn", 1967, 6, 131),
8.
Album("The Final Cut", 1983, 1, 6),
9.
Album("Meddle", 1971, 3, 70),
10.
Album("Atom Hearth Mother", 1970, 1, 55),
11.
Album("Ummagumma", 1969, 5, 74),
12.
Album("A Sauceful of Secrets", 1968, 9, 0),
13.
Album("More", 1969, 9, 153))
14.
15. fun topUSandUK_v1(albums: List<Album>): List<Album> {
16.
val hits = arrayListOf<Album>()
17.
for (i: Int in 0..albums.count()-1) {
18.
if (albums[i].chartUK == 1 && albums[i].chartUS == 1) {
19.
hits.add(albums[i])
20.
}
21.
}
22.
return hits
23. }

Cette fonction utilise une boucle for classique afin de tester une condition sur chacun des albums de la liste.
Cependant, on note ici la prsence de la variable i', utilise comme index de boucle ; il s'agit d'une variable d'tat
que l'on peut aisment liminer en utilisant une structure de type for in :
1. fun topUSandUK_v2(albums: List<Album>): List<Album> {
2.
val hits = arrayListOf<Album>()
3.
for (album in albums) {
4.
if (album.chartUK == 1 && album.chartUS == 1) {
5.
hits.add(album)
6.
}
7.
}
8.
return hits
9. }

En rajoutant un peu de sucre syntaxique, on obtient la fonction suivante :


1. fun topUSandUK_v3(albums: List<Album>): List<Album> {
2.
val hits = arrayListOf<Album>()
3.
albums.forEach {
4.
if (it.chartUK == 1 && it.chartUS == 1) {
5.
hits.add(it)
6.
}
7.
}
8.
return hits
9. }

Suite ces modifications, la boucle for a t remplace par une fonction forEach, qui reprend le mme bloc de code.

-5-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

On se rapproche du fonctionnel, mais un problme demeure, car on continue se concentrer sur la manire de faire
le traitement (le how) et non sur ce que l'on veut rellement faire (le what).
Il reste donc liminer le bloc conditionnel base de ifen utilisant une nouvelle fonction, la fonctionfilter avec un
prdicat :
1. fun topUSandUK_v4(albums: List<Album>): List<Album> {
2.
return albums.filter {
3.
(it.chartUK == 1 && it.chartUS == 1)
4.
}
5. }

Rien de magique dans ce refactoring : on a simplement extrait du code en termes de traitements et de donnes,
puis mis en place une fonction pour les faire interagir, cette fonction filter assurant le parcours de la liste et le test
de la condition.
Il existe beaucoup d'autres fonctions de ce type qui sont des applications du concept de fonctions d'ordre suprieur,
et qui prennent en paramtres d'autres fonctions.
Hadi Hariri nous prsente galement la fonction map, qui permet de raliser simplement des transformations de
donnes :
1. fun topUSandUK_hits_years_v1(albums: List<Album>): List<Int> {
2.
val hits = albums.filter {
3.
(it.chartUK == 1 && it.chartUS == 1)
4.
}
5.
val years = arrayListOf<Int>()
6.
hits.forEach {
7.
years.add(it.year)
8.
}
9.
return years
10. }
11.
12. fun topUSandUK_hits_years_v2(albums: List<Album>): List<Int> {
13.
return albums.filter {
14.
(it.chartUK == 1 && it.chartUS == 1)
15.
}.map {
16.
it.year
17.
}
18. }

Dans cet exemple, on rcupre les annes correspondant aux tops albums et au lieu d'utiliser une boucle for (le
how), on utilise la fonction map laquelle on passe la fonction permettant de rcuprer la proprit year de chaque
album (le what).
Ces refactorings nous permettent galement de rendre le code plus concis et plus expressif, en dlguant une partie
de l'criture de code ceux qui mettent en place les fonctions de base du langage, les frameworks ou les bibliothques
fonctionnelles.
L'approche fonctionnelle permet donc de se concentrer uniquement sur le what, rendant le code plus comprhensible.

IV - Et les patterns Object-Oriented dans tout a ?


Jusque-l rien de bien nouveau si vous connaissez un peu de Scala ou si vous avez eu l'occasion d'utiliser les
Streams de Java 8.
Hadi Hariri s'attaque maintenant certains patterns Object-Oriented, pour nous prouver qu'ils peuvent galement
tre modifis en style fonctionnel, grce l'utilisation de fonctions.

-6-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

IV-A - Patron de mthode (Template Method Pattern)


En approche objet, un patron de mthode dfinit le squelette d'un algorithme l'aide de mthodes abstraites dont le
comportement concret se trouve dans des sous-classes implmentant ces mthodes.
Voici un exemple d'implmentation traditionnelle de ce pattern :
1. public abstract class Record {
2.
public abstract fun editRecord()
3.
public abstract fun persistData()
4.
public fun checkPermissions() {
5.
// Check permissions
6.
}
7.
public fun edit() {
8.
checkPermissions()
9.
editRecord()
10.
persistData()
11.
}
12. }
13.
14. public class CustomerRecord: Record() {
15.
override fun editRecord() {
16.
// Edit customer record
17.
}
18.
override fun persistData() {
19.
// Persist customer data
20.
}
21. }
22.
23. public class InvoiceRecord: Record() {
24.
override fun editRecord() {
25.
// Edit invoice record
26.
}
27.
override fun persistData() {
28.
// Persist invoice data
29.
}
30. }

Une telle hirarchie de classes est-elle rellement ncessaire alors que les seules parties qui diffrent entre les deux
classes d'implmentation sont les fonctions ?
En approche fonctionnelle, l'implmentation de ce pattern devient beaucoup plus simple :
1. public class RecordFunctional(val editRecord: () -> Unit, val persistData: () -> Unit ) {
2.
public fun checkPermissions() {
3.
// Check permissions
4.
}
5.
public fun edit() {
6.
checkPermissions()
7.
editRecord()
8.
persistData()
9.
}
10. }

Les fonctions sont ici directement passes en paramtres, ce qui permet d'liminer tout le code boilerplate de
l'implmentation initiale.

IV-B - Stratgie
Nouvel exemple, avec cette fois le pattern stratgie appliqu des classes dfinissant diffrents algorithmes de tris :
1. public trait SortAlgorithm {
2.
public fun <T> sort(list: List<T>): List<T>
-7-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

3. }
4.
5. public class QuickSort: SortAlgorithm {
6.
override fun <T> sort(list: List<T>): List<T> {
7.
// Processing ...
8.
return sortedList
9.
}
10. }
11.
12. public class BubbleSort: SortAlgorithm {
13.
override fun <T> sort(list: List<T>): List<T> {
14.
// Processing ...
15.
return sortedList
16.
}
17. }
18.
19. public class Sorter(private val algorithm: SortAlgorithm) {
20.
public fun <T> sortList(list: List<T>): List<T> {
21.
return algorithm.sort(list)
22.
}
23. }

En approche objet, on dfinit une interface (trait en Kotlin) et diffrentes classes d'implmentation correspondant
aux diffrents types de tris.
L encore, on peut utiliser une approche fonctionnelle pour passer directement la fonction, et donc le comportement
souhait, la mthode de tri du Sorter :
1. public class SorterFunctional() {
2.
public fun <T> sortList(list: List<T>, sortFunction: (List<T>) -> List<T>): List<T> {
3.
return sortFunction(list)
4.
}
5. }

La fonction sortFunction permet tout simplement de dfinir l'algorithme de tri, liminant une fois de plus la ncessit
de dfinir les diffrentes classes de l'approche objet.

IV-C - Injection de dpendances


L'utilisation de l'injection de dpendances est monnaie courante aujourd'hui dans nos dveloppements.
Voici un exemple de Repository dans lequel on injecte DataAccess, une classe assurant la configuration de l'accs
la base de donnes :
1. public class CustomerRepository(val dataAccess: DataAccess) {
2.
public fun getById(id: Int): Customer {...}
3.
public fun getByName(name: String): List<Customer> {...}
4.
public fun getByEmail(email: String): List<Customer> {...}
5. }

Jusque-l rien d'anormal, les diffrentes mthodes utilisant DataAccess pour raliser les diffrentes requtes.
Cependant, voyons ce qui se passe lorsque le nombre de dpendances augmente, par exemple dans un Controller :
1. public class CheckoutController(val cartRepository: CartRepository,
2.
val shipmentRepository: shipmentRepository,
3.
val taxService: TaxService) {
4.
public fun index() {
5.
// Render Shopping cart with checkout buttons
6.
cartRepository
7.
}
8.
9.
public fun checkout() {
-8-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

10.
11.
12.
13.
14.
15.
16.
17.
18. }

// Render checkout page including data for payment


taxService

public fun shipment() {


// Render shipment page including data for shimpent options
shipmentRepository
}

Ici, chaque mthode utilise une dpendance diffrente, mais il nous faut quand mme dfinir toutes les dpendances.
Cela n'est pas vraiment en accord avec les principes SOLID, non ?
En fait, nous n'avons pas rellement besoin de ces dpendances, simplement de leurs comportements, ce qu'il est
une nouvelle fois possible de refactorer en utilisant une approche fonctionnelle, pour encapsuler ces comportements
dans des fonctions :
1. public fun checkoutHandler(checkoutDataFunc: (Int) -> List<CartEntry>, id: Int) {
2.
val data = checkoutDataFunc(id).take(10)
3.
// Process data
4. }

Hadi Hariri nous dmontre ainsi que l'approche fonctionnelle est parfaitement utilisable dans des scnarios
classiques, que nous avons l'habitude de traiter de manire totalement diffrente dans un style de programmation
objet.

V - Apprendre rflchir fonctionnel


V-A - Des fonctions comme primitives rutilisables du langage
Dans la programmation fonctionnelle, les fonctions sont au cur du langage.
Elles sont des briques essentielles qu'il est possible de combiner via des fonctions d'ordre suprieur, qui prennent
en entre des fonctions et qui peuvent retourner d'autres fonctions :

filter avec un prdicat (une condition) ;


map avec une fonction de transformation ;
groupBy avec une fonction de regroupement par index.

Il est galement possible de raliser du pipeliningpour mettre en place des chanes de traitement :
1. albums.filter { x -> x.year >= 1976 }
2.
.map { x -> Pair(x.title, x.year) }
3.
.groupBy {x -> x.second }

ce niveau, Hadi Hariri nous met en garde vis--vis de ce principe de pipelining afin d'viter de multiplier l'infini
les fonctions dans le pipeline, ce qui aurait pour effet de limiter la lisibilit du code.
Il nous encourage dcouper le pipeline en crant de nouvelles fonctions, aux noms explicites, pour extraire des
regroupements de fonctions de base du pipeline.
Un autre aspect proche du pipelining est la possibilit de raliser des compositions de fonctions de type f(g(x)) :
1. public fun sum(x: Int, y: Int): Int = x + y
2. public fun squared(x: Int): Int = x * x
3.
4. val squaredSum = compose(::sum, ::squared)

-9-

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

Cela permet de respecter :


le principe de Single Responsibility Principle (SRP), appliqu aux fonctions en crivant des fonctions
simples, puis en les composant pour raliser des traitements plus complexes ;
le principe Don't Repeat Yourself (DRY), en crant une nouvelle fonction permettant d'extraire du code
commun plusieurs fonctions, puis en utilisant la composition pour appeler cette nouvelle fonction.

Les notions d'application partielle et de curryfication sont galement trs prsentes en programmation
fonctionnelle :
1.
2.
3.
4.
5.
6.
7.
8.
9.

val sum = { (x: Int, y: Int) -> x + y }


// Partial Function Application
val sumNumberWith10 = sum.partial(10)
println(sumNumberWith10(5)) // -> 15
// Currying
val sumCurried = sum.curry()
println(sumCurried(3)(2)) // -> 5

La curryfication, du nom du mathmaticien Haskell Curry, dsigne l'opration qui permet, partir d'une fonction
plusieurs arguments, d'obtenir une fonction un argument retournant une fonction prenant le reste des arguments,
permettant ainsi l'application partielle.

V-B - Des donnes la recherche de l'immutabilit


Aprs s'tre principalement intress aux fonctions, Hadi Hariri souhaite dsormais revenir sur la notion de donnes.
Ces donnes peuvent tre de diffrents types dans une approche fonctionnelle :

des scalaires : ge, date de naissance, montant ;


des collections de types abstraits (Abstract Data Type) : liste de clients, liste de factures

Ces listes sont les entres et les sorties des fonctions vues prcdemment (map, filter) et les scalaires peuvent
tre obtenus partir de listes via d'autres fonctions (first, last, find, aggregate).
Hadi Hariri nous prsente alors plus particulirement la fonction fold, qui permet d'appliquer une transformation sur
les lments d'une collection, en collectant les rsultats au fur et mesure via un accumulateur. Une fois que l'on a
travers toute la liste, seul l'accumulateur reste, c'est la valeur scalaire laquelle on a rduit la liste :

Par exemple, pour dterminer le maximum dans une liste d'entiers :


1. public fun maximumFold(list: List<Int>) : Int {
2.
return list.fold(0, {x, y -> Math.max(x, y)})
3. }
- 10 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

La programmation fonctionnelle dispose galement de deux outils particulirement intressants : le pattern


matching et la rcursivit.
1. public fun maximumRecursive(list: List<Int>): Int {
2.
when (list.count()) {
3.
0 -> throw IllegalArgumentException("empty list!")
4.
1 -> return list[0]
5.
else -> return Math.max(list[0], maximumRecursive(tail(list)))
6.
}
7. }

Comme on le voit dans la fonction ci-dessus, le pattern matching fournit une manire simple de construire des
structures alternatives. On est en effet bien loin des sries de if else if ou de switch case, que nous devons
utiliser en Java pour rpondre ce type de problmatique.
La rcursivit n'est quant elle pas limite la programmation fonctionnelle, mais l'utilisation de structures rcursives
est beaucoup plus courante dans ce type de programmation que dans le style de programmation imprative, o
l'utilisation des boucles de type for est plus habituelle.
L'utilisation de la rcursivit permet galement de favoriser l'immutabilit des donnes, en vitant l'utilisation de
variables d'tat.
D'ailleurs, afin de limiter ces variables d'tat, Hadi Hariri nous conseille galement de traiter les listes comme des
structures infinies, rendant possibles l'valuation paresseuse (Lazy evaluation) et la programmation ractive.
Lorsque l'on fait de la programmation fonctionnelle, nous devons toujours rechercher privilgier l'immutabilit des
donnes, par exemple en crant une nouvelle liste plutt qu'en modifiant une liste existante lors de l'application d'un
traitement sur celle-ci.

VI - Programmation fonctionnelle et performances


Hadi Hariri s'intresse maintenant l'aspect performance de la programmation fonctionnelle.

VI-A - Mmozation
Il utilise tout d'abord l'exemple du calcul de la suite de Fibonacci, rsolu ici dans un style purement rcursif :
1. public fun fibonacciRecursive(n: Int) : Long {
2.
if (n == 0 ||n == 1) {
3.
return n.toLong()
4.
} else {
5.
return fibonacciRecursive(n -1) + fibonacciRecursive(n - 2)
6.
}
7. }

Une premire technique d'optimisation est l'utilisation de la mmozation, qui consiste rduire le temps d'excution
d'une fonction en mmorisant ses rsultats d'une fois sur l'autre.
Pour ce faire, on introduit tout simplement un cache qui va viter le recalcul des valeurs dj calcules lors de
rcursions intermdiaires :
1. val cache = hashMapOf<Int, Long>(0 to 0, 1 to 1)
2.
3. public fun fibonacciMemoization(n: Int) : Long {
4.
if (cache.containsKey(n)) {
5.
return cache.get(n)!!.toLong()
6.
} else {
7.
val result = fibonacciMemoization(n -1) + fibonacciMemoization(n - 2)
- 11 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

8.
9.
10.
11. }

cache.set(n, result)
return result

VI-B - Rcursion terminale


Comme on a dj pu l'voquer prcdemment, la programmation fonctionnelle utilise de nombreuses structures
rcursives, ce qui peut poser des problmes de gestion de la pile, la zone mmoire rserve l'excution d'un
programme.
En effet, dans une procdure rcursive, toutes les variables locales sont stockes dans la pile d'excution et empiles
autant de fois qu'il y a d'appels rcursifs, avant d'tre dsempiles au fur et mesure qu'on remonte les niveaux une
fois la condition de sortie rencontre.
Ainsi, si on ne fait pas attention la profondeur de la rcursivit, la pile se remplit progressivement jusqu' atteindre
sa limite de taille, ce qui entrane le problme bien connu de dbordement de pile : stack overflow.
Afin de limiter ces impacts, on peut utiliser la technique de rcursion terminale (tail recursion). Une fonction
rcursivit terminale (tail-recursive) est une fonction o l'appel rcursif est uniquement la dernire instruction tre
value.
1. public fun factorial(number: Int): Int {
2.
when (number) {
3.
0,1 -> return 1
4.
else -> return number * factorial(number - 1)
5.
}
6. }

La fonction de calcul de factorielle ci-dessus ne l'est pas, car le rsultat de l'appel rcursif factorial (number - 1)
est utilis par la fonction *. cause de cela, le rsultat de factorial (number - 1) doit tre conserv dans la pile
d'excution pour tre utilis par *.
Cependant il est possible de modifier l'implmentation de cette fonction pour la rendre tail-recurvive :
1. public fun factorialTailCall(number: Int): Int {
2.
return factorialTC(number, 1)
3. }
4.
5. public fun factorialTC(number: Int, accumulator: Int): Int {
6.
when (number) {
7.
0 -> return accumulator
8.
else -> return factorialTC(number - 1, accumulator * number)
9.
}
10. }

Ici, la fonction factorialTC s'appelle elle-mme uniquement lors de sa dernire instruction, ce qui permet d'conomiser
de l'espace mmoire, car aucun tat, sauf l'adresse de la fonction appelante, n'a besoin d'tre sauv sur la pile
d'excution.
Ainsi, alors que l'espace consomm sur la pile d'excution augmente linairement lors de l'excution rcursive, il
reste constant aprs l'optimisation tail-recursive, diminuant ainsi nettement l'empreinte mmoire.
Le recours un accumulateur (accumator dans l'implmentation tail-recursive de factorielle) est une technique
couramment utilise pour l'criture de fonctions tail-recursive.
noter qu'en Kotlin, il est ncessaire de prciser via une annotation tailRecursive que l'on souhaite utiliser une
optimisation de type tail-recursive, appele Tail Call Optimization :

- 12 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

1. tailRecursive
2. public fun factorialTC1(number: Int, accumulator: Int): Int {
3.
when (number) {
4.
0 -> return accumulator
5.
else -> return factorialTC1(number - 1, accumulator * number)
6.
}
7. }

Cela permet d'indiquer au compilateur que l'on souhaite faire cette optimisation et donc de vrifier si celle-ci est
effectivement ralisable afin d'alerter le dveloppeur si ce n'est pas le cas.
Attention nanmoins, car tous les langages fonctionnels ne prennent pas en charge ce type d'optimisation ! Par
exemple, Java supporte les appels tail-recursive, mais il n'y apporte aucune optimisation, car celle-ci n'est pas
disponible dans la JVM, contrairement Scala o le compilateur est capable de raliser cette optimisation (via
l'annotation @tailrec).

VI-C - Fonctions inline


Enfin, Hadi Hariri avoue que les fonctions d'ordre suprieur peuvent avoir des impacts ngatifs sur les performances.
En effet, en Kotlin, chaque fonction est un objet qui capture une closure, l'ensemble des variables qui sont accessibles
dans le corps de la fonction. L'allocation mmoire pour ces fonctions et ces variables, ainsi que les appels virtuels,
introduisent donc invitablement un surcot l'excution.
Afin de limiter cet impact, il est possible de dclarer ces fonctions comme inline ; dans ce cas, le code complet de
la fonction (ainsi que des lambdas utiliss) est insr dans le code de l'appelant l'excution.
L encore, tous les langages ne disposent pas de cette possibilit de dclarer des fonctions comme inline.
En Java, il n'est pas possible de suggrer au compilateur qu'une fonction doit tre inline, mais la JVM
ralise nanmoins ses propres optimisations l'excution, ce qui permet de limiter les impacts du paradigme
fonctionnelle .

VII - Quid de tous les concepts effrayants de la programmation fonctionnelle ?


Comme vous avez pu vous en rendre compte, mis part la curryfication et l'application partielle, aucun des termes
habituellement utiliss par les puristes de la programmation fonctionnelle n'a t cit dans cette prsentation.
Avant de conclure sa prsentation, Hadi Hariri dcide nanmoins d'voquer rapidement certains de ces termes.

VII-A - Foncteurs (Functors)


Les foncteurs sont des lments sur lesquels on peut raliser une transformation, par exemple une collection de a
o l'on peut appliquer une fonction a -> b pour retourner une collection de b.
La fonction map est donc un exemple simple de foncteur que nous avons dj eu l'occasion de manipuler
prcdemment.

VII-B - Monades
Une monade permet d'encapsuler une valeur, un traitement ou un rsultat potentiel. Il est ainsi possible de voir une
monade comme une bote, vide ou ayant un contenu, qui nous fournit des abstractions et des oprations au-dessus
de la valeur ventuellement encapsule.

- 13 -

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

La programmation fonctionnelle par Jerme Valloire

On peut citer diffrentes monades usuelles :

la monade Option, appele aussi monade Maybe permettant d'enchaner des traitements unitaires pouvant
potentiellement ne pas retourner de valeur. Scala utilise le type Option permettant de caractriser la
prsence ou l'absence de valeur via deux sous-types, Some[T] ou None ;
la monade List, permettant d'appliquer des oprations unitaires sur des ensembles de valeurs. On peut ici
citer le type IEnumerable<T> de C# ;
les Futures,permettant d'encapsuler un rsultat qui arrivera plus tard et qui sont utiliss pour raliser des
oprations asynchrones ou en programmation ractive.

Ces termes thoriques cachent des concepts mathmatiques, mais comme le souligne Hadi Hariri, nul besoin de les
matriser pour adopter les concepts de la programmation fonctionnelle prsents lors de sa confrence et commencer
ainsi refactoriser votre code.
Si vous tes intress par ces concepts, je vous invite parcourir le web o les ressources sont plus que nombreuses,
mais encore une fois, le plus important, c'est de savoir les mettre en pratique de manire concrte.

VIII - Conclusion
En conclusion, Hadi Hariri nous encourage fortement exprimenter la programmation fonctionnelle et ainsi arrter
de rflchir uniquement en termes d'objets en adoptant les fonctions comme des lments primitifs du langage.
L'utilisation de fonctions devrait nous permettre de nous concentrer sur diffrents points :

crire moins de code ;


crire du code plus descriptif (le what plutt que le how) ;
crire du code plus facile comprendre en sparant donnes et traitements ;
crire du code plus dterministe en favorisant l'immutabilit et en limitant les effets de bord.
It is not Rocket science !!! (Hadi Hariri)

Pourquoi chercher continuellement opposer programmation oriente objet et programmation fonctionnelle ? Pour
moi, les deux paradigmes ne sont pas l pour s'opposer, mais pour se complter, libre au bon dveloppeur d'utiliser
le bon outil pour rsoudre le bon problme.
Tout comme Hadi Hariri, je ne peux que vous conseiller d'exprimenter la programmation fonctionnelle. C'est
effectivement une manire diffrente de coder, mais avec le temps, l'approche fonctionnelle devient de plus en plus
naturelle et si cela peut faire de vous un meilleur codeur, alors pourquoi ne pas essayer ?

IX - Remerciements
Cet article a t publi avec l'aimable autorisation de

SOAT, socit d'expertise et de conseil en informatique.

Nous tenons remercier Claude LELOUP pour la relecture orthographique et


mise au gabarit.

- 14 -

Marie-Hlne Delacroix pour la

Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de
prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2015 Jerme Valloire. Aucune reproduction,
mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts.
http://soat.developpez.com/tutoriels/programmation/programmation-fonctionnelle/

Vous aimerez peut-être aussi