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
Made with Slides.com