La programmation fonctionnelle,
à quoi ça rime ?
@JoGrenat
Disclaimer
Kesako?
function() {
}
Mutabilité
Effets de bord
Mutabilité
const items = ['Ballon', 'Fourchette'];
items.push('Clé');
console.log(items);
// ['Ballon', 'Fourchette', 'Clé']
const items = ['Ballon', 'Fourchette'];
const newItems = [...items, 'Clé'];
console.log(items);
// ['Ballon', 'Fourchette']
Effets de bord
let currentUser = null;
async function createUser() {
const user = { name: 'Jordane' };
await db.add(user);
currentUser = user;
console.log('User created');
}
await createUser();
Fonction pure
function add(a, b) {
return a + b;
}
add(1, 3); 4
add(1, 3); 4
Mutabilité
Effets de bord
LANGAGE UTILE
LANGGE UTILE
LANGGE UTLE
LANGE UTLE
LNGE UTLE
LNGE ULE
LNG ULE
NG ULE
NGULE
NGUL
NUL
GOTO Considered Harmful
Poka-Yoke
Avoid
Mistakes
Mutabilité
Effets de bord
Mutabilité
import moment from 'moment';
const talkDate = moment('2024-07-04 11:20:00');
const alarm = talkDate.subtract(10, 'minutes');
setAlarm(alarm);
startPresentationAt(talkDate);
Effets de bord
function createUser(name) {
db.createUser({ name, createdAt: moment() });
}
createUser('Jordane');
function createUser(name) {
db.createUser({ name, createdAt: moment() });
}
createUser('Jordane');
{ name: 'Jordane', createdAt: '2024-01-02' }
it('should create user', async () => {
// Given
db = mockDatabase();
// When
await createUser('Jordane');
// Then
const users = await db.getAll();
expect(users[0]?.name).toEqual('Jordane');
// After
cleanDatabaseMock();
});
La perfection n'est pas atteinte quand il n'y a plus rien à ajouter, mais quand il n'y a plus rien à enlever.
Antoine de Saint-Exupéry
Programme
Functional
Core
Imperative
Shell
https://www.youtube.com/watch?v=P1vES9AgfC4
Scott Wlaschin
Add
Add
Add
Add
Functional
Core
Imperative
Shell
function add(a, b) {
return a + b;
}
function add(a, b) {
return a + b;
}
async function shell() {
const a = await askUser('First number?');
const b = await askUser('Second number?');
const result = add(a, b);
console.log(`${a} + ${b} = ${result}`);
}
Programme
Runtime
Donnée
Donnée
{ method: 'GET', url: 'https://www.perdu.com' }
{ status: 200, content: 'Vous êtes perdu ?' }
Tout est donnée, même les fonctions
function multiplyBy2(a) {
return a * 2;
}
[1, 2, 3].map(multiplyBy2);
first-class citizen
Tout est expression
Même les conditions
function isMajeur(age) {
return age >= 18
? 'Majeur·e'
: 'Mineur·e';
}
function isMajeur(age) {
if (age > 18) {
console.log('Majeur·e');
}
}
function factorielle(until) {
let result = 1;
for(let i = 1; i <= until; i++) {
result *= i;
}
return result;
}
function factorielle(number) {
return number < 2
? 1
: number * factorielle(number - 1);
}
Mutabilité
Effets de bord
Statements
Fonctions
Fonction
Lambda-calcul
const numbers = [1, 2, 3];
numbers.map(x => x * 2);
const add = (a, b) => a + b;
add(1, 3) === 4;
const add = (a) => {
return (b) => a + b
}
add(1)(3) === 4;
const add3 = add(3);
add3(1) === 4;
Currification
Application partielle
-
map
-
filter
-
reduce
const map = (fn) => (array) => {
// Transform values
}
const getNames = map(user => user.name);
getNames(myUsers);
const double = x => x * 2;
const add = x => y => x + y;
const result = add(4)(double(3)); // 10
const result = 3
|> double
|> add(4)
(3)
(6)
= 6
Pipe
const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|> filter(n => n % 2 === 0)
|> map(a => a * 10);
Pipe 🤝 Currification 🤝 Application partielle
Statements
const noUnderage = (age: number) {
if (age < 18) {
throw new Error('Vous devez être majeur·e pour voter');
}
return 'Allez voter dimanche !';
}
type Either =
| { success: true, value: string; }
| { success: false, error: Error; }
const noUnderage = (age: number): Either {
if (age < 18) {
return {
success: false,
error: new Error('Vous devez être majeur·e pour voter')
};
}
return { success: true, value: 'Allez voter dimanche !' };
}
type Either<E, A> =
| { type: 'left', value: E }
| { type: 'right', value: A }
const divide = (num: number) => (den: number): Either<string, number> => {
return den === 0
? E.left('Cannot divide by 0')
: E.right(num / den);
}
const result = divide(6, 2);
function divide: number -> number -> Either<string, number>
const add3 = (a: number) => a + 3;
const result =
divide(6)(2)
|> add3
const add3 = a => a + 3;
const result =
divide(6)(2)
|> E.map(add3)
const result = [1, 2, 3]
|> map(x => x * 2)
Monades
A
B
f(A) → B
Either
Array
Promise
getUser().then(user => user.name);
Théorie des Catégories
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. [...] This has led to innumerable errors, vulnerabilities, and system crashes [...]
– Tony Hoare –
number
3.17
-6
0
98438I9
...
boolean
true
false
null
null
Mutabilité
Effets de bord
Statements
Null & undefined
Option
const getUser = (id: number) => (users: User[]): Option<User> => {
const user = users.find(user => user.id === id);
return user ? O.some(user) : O.none;
}
const username = myUsers
|> getUser(123)
|> O.map(user => user.name);
IO
getLine
|> IO.map(text => writeText(`Vous avez écrit: ${text}`));
Ce qui semble compliqué, ce sont les outils
Améliorer la lisibilité et réduire la charge cognitive
Rendre explicites les dépendances
Supprimer toute une gamme d'erreurs
Utiliser des outils prouvés
Mettre les données et leur transformation au centre
Avoir un code testable
Comment
commencer ?
Elm: A delightful language
for reliable web applications.
fp-ts: Bibliothèque fonctionnelle pour TypeScript
Merci !
@JoGrenat
La programmation fonctionnelle, à quoi ça rime ?
By ereold
La programmation fonctionnelle, à quoi ça rime ?
- 222