Password hola#1234
SSID devoxxfr-hol
fp-ts
Quand TypeScript devient
fonctionnel
Jordane Grenat
Johan Rouve
Bastien Tran
Agenda
13h30 - Introduction
13h45 - Exercices - tome 1
15h00 - Pause
15h15 - Exercices - tome 2
Programmation fonctionnelle ?
- Fonctions
- Pureté
- Immutabilité
const todoList = [
{ task: 'Give a talk at Devoxx' }
];
const addTodo = (task) => {
todoList.push({ task: task });
};
addTodo('Visit Canada');
const todoList = [
{ task: 'Give a talk at Devoxx' }
];
const addTodo = (task, todoList) => {
return [...todoList, { task: task }];
};
const updatedList = addTodo('Visit Canada', todoList);
const sendEmail = (user) => {
if (user.email === null) {
throw new Error('No email provided for the user');
}
launchNuclearMissile();
}
Honnêteté
const names = ['Johan', 'Bastien', 'Jordane'];
const uppercaseNames = [];
for (const name of names) {
uppercaseNames.push(name.toUppercase());
}
const names = ['Johan', 'Bastien', 'Jordane'];
const toUppercase = name => name.toUppercase();
const uppercaseNames = names.map(toUppercase);
Outils
- fonctions
- map, filter, reduce
- Functor
- Applicative
- Monad
const log = (level, prefix, message) => {
// ...
};
log('error', 'Authentication', 'Bad login/password');
const log = level => prefix => message => {
// ...
};
const logError = log('error');
const logAuthenticationError = logError('Authentication');
logAuthenticationError('Bad login/password');
tryLogin(login, password)
.onError(logAuthenticationError);
tryLogin(login, password)
.onError(error => log('error', 'Authentication', error));
const filter = (predicate) => (array) => //...
const getActiveUsers = filter(user => user.status === 'active');
const activeUsers = getActiveUsers(allUsers);
const getFruits = filter(item => item.type === 'fruit');
const allFruits = getFruits(allItems);
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 filter = (predicate) => (array) => //...
const groupBy = (accessor) => (array) => // ...
const users = [
{ name: 'Marcia', city: 'Paris', hobby: 'music' },
{ name: 'Nicolas', age: 'Tours', hobby: 'music' },
{ name: 'Camille', age: 'Paris', hobby: 'theater' }
];
const byHobby = users
|> filter(user => user.city === 'Paris')
|> groupBy(user => user.hobby);
const parisians = filter(user => user.city === 'Paris')(users);
const byHobby = groupBy(user => user.hobby)(parisians);
// {
// music: [{ name: 'Marcia', city: 'Paris', hobby: 'music' }],
// theater: [{ name: 'Camille', age: 'Paris', hobby: 'theater' }]
// }
fp-ts
Disclaimer. Teaching functional programming is out of scope of this project, so the documentation assumes you already know what FP is.
It includes the most popular data types, type classes, and abstractions from languages like Haskell, PureScript, and Scala.
const findUser = (userId) => {
return db.query(`SELECT * FROM users WHERE userId = ${userId}`);
}
Et les types ?
const findUser = (userId: number): Promise<User | undefined> => {
return db.query(`SELECT * FROM users WHERE userId = ${userId}`);
}
const filter = (predicate) => (array) => //...
const groupBy = (accessor) => (array) => // ...
const byHobby = users
|> filter(user => user.city === 'Paris')
|> groupBy(user => user.hobby);
import * as A from 'fp-ts/Array';
import * as NEA from 'fp-ts/NonEmptyArray';
const byHobby = users
|> A.filter(user => user.city === 'Paris')
|> NEA.groupBy(user => user.hobby);
import { pipe } from 'fp-ts/function';
import * as A from 'fp-ts/Array';
import * as NEA from 'fp-ts/NonEmptyArray';
const byHobby = pipe(
users,
A.filter(user => user.city === 'Paris'),
NEA.groupBy(user => user.hobby)
);
import { pipe } from 'fp-ts/function';
import * as A from 'fp-ts/Array';
import * as NEA from 'fp-ts/NonEmptyArray';
Normes d'import
Modules
- Array
- NonEmptyArray
- Option
- Either
- Record
- Task
- Reader
- ...
Option
const findUser = (userId: number): User | undefined => {
// ...
};
const findUser = (userId: number): Option<User> => {
// ...
};
type Option<A> =
| { _tag: 'Some', value: A }
| { _tag: 'None' };
import * as O from 'fp-ts/Option';
const username = pipe(
userId,
findUser,
O.map(user => user.name)
);
Tous en boite !
Option : une boîte contenant zéro ou une valeur
Array : une boîte contenant zéro, une ou plusieurs valeurs du même type
NonEmptyArray : une boîte contenant une ou plusieurs valeurs du même type
map : transformer ce qu'il y a dans la boite
- Cloner le repository
-
npm install
-
npm run test
- Réactiver les test un à un
- Les faire passer au vert
Password hola#1234
SSID devoxxfr-hol
Une boîte contenant une valeur d'un type ou d'un autre
Either
type Either<E, A> = Left<E> | Right<A>
Très souvent, le Either représente une erreur : soit tout est Right, soit on a une erreur en Left.
Une boîte qui contient ou contiendra une valeur
Task
type Task<A> = () => Promise<A>
Une Task ne peut pas échouer
Une boîte qui contient ou contiendra une valeur d'un type E ou d'un type A
TaskEither
type TaskEither<E, A> = () => <Either<E, A>>
Have
a
break !
À Gagner
Wallaby (2)
TDD lovers
Quokka (2)
IDE sandbox
Console ninja (2)
Console addicts
À l'origine, il y eut le Magma
Un ensemble avec une
loi de composition interne
Exemple : Ensemble des entiers naturels N (N, +) est un magma, car pour tout a + b = c, c appartient aussi à N
Cette loi est une fonction, souvent appelée concat
Ça peut très vite devenir un Semi-groupe
Si la loi de composition est
associative
Exemple : (N, +) est un semi-groupe, a + (b + c) = (a + b) + c
Voire même un Monoïde
S'il existe un élément
neutre
Exemple : (N, +) est un monoïde, car 0 est l'élément neutre : 0 + a = a
On ne joue pas tous dans la même Catégorie
Et une Applicative ?
C'est une boite
Boîte M avec deux fonctions : - unit : a => M a - bind ou flatMap : (a => M b) => M a => M b
Mais Jamy, c'est quoi une
Monade ?
C'est une boite
Boîte M avec deux fonctions : - unit : a => M a - bind ou flatMap : (a => M b) => M a => M b
Workshop fp-ts
By ereold
Workshop fp-ts
- 47