Back to flin
flin

L'opérateur pipeline : composition fonctionnelle dans FLIN

Comment nous avons implémenté l'opérateur pipeline dans FLIN -- la syntaxe |> pour la composition fonctionnelle, sa transformation en appels de fonctions par le parser, et l'expérience développeur qu'il débloque.

Thales & Claude | March 30, 2026 3 min flin
EN/ FR/ ES
flinpipelinefunctionalcomposition

La Session 150 a ajouté une fonctionnalité techniquement simple -- environ 30 lignes de code à travers trois fichiers -- mais disproportionnellement impactante pour la lisibilité des programmes FLIN. L'opérateur pipeline.

L'idée est empruntée à Elixir, F# et l'opérateur pipeline (éternellement) proposé en JavaScript : prendre une valeur à gauche, la passer comme premier argument à la fonction à droite. Enchaîner autant que vous voulez. Lire de gauche à droite, comme de la prose.

flin5 |> double |> add_one |> square

Ceci s'évalue à square(add_one(double(5))). Même résultat. Expérience de lecture entièrement différente. La version imbriquée se lit de l'intérieur vers l'extérieur. La version pipeline se lit de gauche à droite.

Pourquoi les pipelines comptent

Considérez une chaîne de transformation de données sans pipelines :

flinresult = format(sort(filter(users, u => u.active), u => u.name), "table")

Maintenant avec les pipelines :

flinresult = users
    |> filter(u => u.active)
    |> sort(u => u.name)
    |> format("table")

La version pipeline raconte une histoire : prendre les utilisateurs, filtrer les actifs, trier par nom, formater en tableau.

Parser : désucrage en appels de fonctions

Le parser est où le vrai travail se passe. L'opérateur pipeline est du sucre syntaxique -- il ne crée pas un nouveau noeud AST. Au lieu de cela, le parser transforme a |> f(b, c) en f(a, b, c) pendant l'analyse.

Cette approche de désucrage a un avantage significatif : chaque passe du compilateur après le parser (vérificateur de types, générateur de code, optimiseur) ne voit jamais un opérateur pipeline. Elles voient un appel de fonction. Aucun changement nécessaire en aval.

rustfn parse_pipeline(&mut self) -> Result<Expr, ParseError> {
    let mut expr = self.parse_logical_or()?;

    while self.match_token(&Token::PipeArrow) {
        let right = self.parse_postfix()?;

        expr = match right {
            Expr::Call { callee, mut args, span } => {
                args.insert(0, expr);
                Expr::Call { callee, args, span }
            }
            Expr::Identifier { name, span } => {
                Expr::Call {
                    callee: Box::new(Expr::Identifier { name, span }),
                    args: vec![expr],
                    span,
                }
            }
            _ => {
                return Err(ParseError::new(
                    "pipeline right-hand side must be a function or function call",
                    self.current_span(),
                ));
            }
        };
    }

    Ok(expr)
}

Patterns de pipeline réels

flinreport = transactions
    |> filter(t => t.date > last_month)
    |> group_by(t => t.category)
    |> map_values(sum)
    |> sort_by_value("desc")
    |> take(10)
    |> format_table()

slug = title
    |> trim()
    |> lower()
    |> replace(" ", "-")
    |> replace("--", "-")
    |> truncate(50)

Ceci est la partie 38 de la série « Comment nous avons construit FLIN ».

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles