Skip to content

3. L'Opérateur Pipe |>

Le Problème

Code difficile à lire

// Calculer la taille de la liste renversée des pairs
let resultat = taille(renverse(pairs(Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Vide))))))))

Problème : On doit lire de l'intérieur vers l'extérieur !

1. pairs(...)        --> [2, 4]
2. renverse([2, 4])  --> [4, 2]
3. taille([4, 2])    --> 2

Mais le code s'écrit dans l'ordre inverse de la lecture !


La Solution : L'Opérateur |>

Syntaxe

valeur |> fonction

Se lit : "Prends valeur et applique-lui fonction"

Exemple Simple

// Sans pipe
taille(Cons(1, Cons(2, Cons(3, Vide))))
// -> 3

// Avec pipe
Cons(1, Cons(2, Cons(3, Vide))) |> taille
// -> 3

Les deux sont équivalents !


Lire de Gauche à Droite

Chaîner des Opérations

// ❌ Sans pipe : lecture compliquée
let resultat = taille(renverse(pairs(lst)))

// ✅ Avec pipe : lecture naturelle
let resultat =
  lst
  |> pairs      // [2, 4]
  |> renverse   // [4, 2]
  |> taille     // 2

Ordre de lecture = ordre d'exécution !

Visualisation

lst = [1, 2, 3, 4, 5]
    ↓ pairs
[2, 4]
    ↓ renverse
[4, 2]
    ↓ taille
2

Comment Ça Marche ?

Définition

// x |> f   est exactement équivalent à   f(x)

La valeur à gauche du |> devient le premier argument de la fonction à droite.

// Ceci
lst |> taille

// Est transformé en cela
taille(lst)

Avec des Fonctions à Plusieurs Paramètres

// take : fn(Int, Liste(a)) -> Liste(a)
//             ↑ premier argument = la liste piped

lst |> take(3)
// devient : take(lst, 3)  ← lst est inséré en PREMIÈRE position

⚠️ En Gleam, la valeur piped devient toujours le premier argument. C'est pourquoi les fonctions Gleam placent la donnée principale en premier paramètre.


Exemples Pratiques

Exemple 1 : Traitement de Liste

// Calculer la somme des nombres pairs
let resultat =
  lst
  |> pairs    // [2, 4, 6]
  |> somme    // 12

// Équivalent sans pipe
let resultat = somme(pairs(lst))

Exemple 2 : Avec des Fonctions à Plusieurs Paramètres

// take : fn(Liste(a), Int) -> Liste(a)

lst
|> take(3)    // take(lst, 3) → [1, 2, 3]
|> somme      // 6

// Équivalent
somme(take(lst, 3))

Exemple 3 : Pipeline Complexe

pub fn traiterDonnees(lst: Liste(Int)) -> Int {
  lst
  |> pairs        // Garde les pairs
  |> take(3)      // Prends les 3 premiers
  |> dupliquer    // Duplique chaque élément
  |> somme        // Additionne tout
}

Exemple 4 : Avec des Lambdas

lst
|> fn(l) { take(l, 2) }    // Lambda explicite
|> somme

// Ou plus directement
lst
|> take(2)
|> somme

Style et Lisibilité

Quand Utiliser le Pipe ?

✅ Oui : Plusieurs Transformations

let resultat =
  maListe
  |> fonction1
  |> fonction2
  |> fonction3

✅ Oui : Opérations Séquentielles Claires

lst
|> pairs
|> somme
|> int.to_string

❌ Non : Une Seule Fonction

// Pas nécessaire
let resultat = lst |> taille

// Plus simple
let resultat = taille(lst)

Indentation et Format

Style Recommandé

// Chaque étape sur une ligne, alignées
let resultat =
  lst
  |> pairs
  |> renverse
  |> taille

Avec Commentaires

pub fn traiterCommande(commande: Liste(Int)) -> Int {
  commande
  |> pairs              // Filtrer les quantités paires
  |> take(10)           // Maximum 10 articles
  |> somme              // Total de la commande
  |> fn(x) { x * 2 }   // Doubler pour promotion
}

Cas Pratiques dans Tes Exercices

Exemple 1 : Tester des Fonctions

// Au lieu de
let test1 = taille(renverse(pairs(lst_ex1)))

// Avec pipe
let test1 =
  lst_ex1
  |> pairs
  |> renverse
  |> taille
// Beaucoup plus clair ce qu'on teste !

Exemple 2 : Combiner avec concat

// Concaténer puis traiter
pub fn combineEtTraite(lst1: Liste(Int), lst2: Liste(Int)) -> Int {
  concat(lst1, lst2)   // Combine les listes
  |> pairs             // Garde les pairs
  |> somme             // Additionne
}

Exemple 3 : Avec option

import gleam/option

// Obtenir le double du premier élément, ou 0
lst
|> premier                          // Option(Int)
|> option.map(fn(x) { x * 2 })     // Option(Int)
|> option.unwrap(or: 0)             // Int

Questions Fréquentes

Q1 : Puis-je mélanger parenthèses et pipes ?

Oui !

let resultat =
  take(lst, 3)          // expression entre parenthèses
  |> somme
  |> fn(x) { x * 2 }

Q2 : La valeur piped est toujours en première position ?

Oui, toujours en Gleam. C'est différent d'Elm où elle va en dernière position. En Gleam, les fonctions sont conçues pour recevoir leur argument principal en premier :

// fn(liste, n) → liste en premier → compatible avec |>
lst |> take(3)      // take(lst, 3) ✅

// Si la fonction avait l'ordre inverse : fn(n, liste)
// le pipe ne fonctionnerait pas directement

Q3 : Peut-on utiliser |> avec ses propres fonctions ?

Oui, avec n'importe quelle fonction !

pub fn maFonction(x: Int) -> Int {
  x * 2 + 1
}

5 |> maFonction
// -> 11

Exercices de Compréhension

Transforme ces expressions avec |> :

Exercice 1

// Sans pipe
taille(renverse(lst))

// Avec pipe
lst |> renverse |> taille

Exercice 2

// Sans pipe
somme(pairs(take(lst, 5)))

// Avec pipe
lst
|> take(5)
|> pairs
|> somme

Exercice 3

// Sans pipe
taille(concat(lst1, lst2))

// Avec pipe
concat(lst1, lst2)
|> taille

Comparaison avec d'Autres Langages

Unix (l'inspiration originale)

cat fichier.txt | grep "mot" | wc -l

Python (méthodes chaînées)

# Python : le pipe est limité aux méthodes des objets
[1, 2, 3, 4, 5]
    .filter(lambda x: x % 2 == 0)   # pas natif en Python !

JavaScript

[1, 2, 3, 4, 5]
    .filter(x => x % 2 === 0)
    .map(x => x * 2)
    .reduce((a, b) => a + b, 0)

Gleam

lst
|> pairs                          // filtre les pairs
|> map(fn(x) { x * 2 })          // double chaque élément
|> somme                          // additionne

Même principe, mais universel : fonctionne avec n'importe quelle fonction, pas seulement les méthodes d'un objet.


Résumé : Pourquoi Utiliser |> ?

Avantages

  1. Lisibilité : Ordre naturel gauche → droite
  2. Clarté : Chaque transformation est explicite
  3. Débogage : Facile de commenter une étape intermédiaire
  4. Style : Code Gleam idiomatique

Le Pattern

donnees
|> transformation1
|> transformation2
|> transformation3

Se lit comme une recette de cuisine !


Conseil Final

Utilise |> quand tu as 2+ transformations séquentielles.

// ✅ Bon usage
let resultat =
  lst
  |> pairs
  |> somme

// ❌ Pas nécessaire
let resultat = lst |> taille

// ✅ Mieux
let resultat = taille(lst)