URL:     https://linuxfr.org/news/decouverte-du-langage-elixir
Title:   Découverte du langage Elixir
Authors: Thomas J.
         bobble bubble et Xavier Teyssier
Date:    2026-10-30T14:10:20+01:00
License: CC By-SA
Tags:    tutoriel, programmation_fonctionnelle et elixir
Score:   8


Beaucoup de Rust par-ci... Un peu de Go par-là... Saupoudrez de LLM selon vos goûts et vous aurez une bonne photographie des sujets qui préoccupent les développeurs sur l'Internet d'aujourd'hui.
Alors, n'est-ce pas le meilleur moment pour se rafraîchir l'esprit avec des langages plus discrets ?
Je vous propose ici une présentation accélérée d'Elixir.

----

[Site officiel du langage Elixir](https://elixir-lang.org/)

----

# Un langage de programmation fonctionnelle
Ceux qui ont déjà fait de la programmation orientée objet organisent principalement leur code autour d'objets et d'instances. Cette approche a ses avantages, mais cela nécessite d'encapsuler la donnée et la gestion des changements d'état peut parfois devenir complexe.
De son côté, la programmation fonctionnelle privilégie les fonctions qui, au lieu de masquer les données, transforment simplement des entrées en sorties.
Elixir s'appuie essentiellement sur ce paradigme.



# Correspondance de motifs (ou _Pattern matching_)
En Elixir, le signe égal n'est pas une affectation mais plutôt une [assertion](https://fr.wikipedia.org/wiki/Assertion).
Ainsi, on n'affecte ou ne modifie la valeur que de la variable située à gauche du signe égal. À droite, la variable est remplacée par sa valeur. Exemple avec `iex`, la console interactive :


```elixir
iex> a = 2
2
iex> 2 = a
2
iex> 1 = a
** (MatchError) no match of right hand side value: 2
```



Cette ligne de code échoue car elle équivaut à `1 = 2`, ce qui provoque une erreur.
À noter que le caractère `_` sert à ignorer une valeur, ainsi :


```elixir
iex> [1, _, _] = [1, 2, 3]
[1, 2, 3]
```



La correspondance de motifs en Elixir est similaire à celle d'Erlang (ces deux langages utilisent la même [VM](https://fr.wikipedia.org/wiki/BEAM_(machine_virtuelle))).
Joe Armstrong, le créateur d'Erlang, compare ce signe égal à celui utilisé en algèbre : lorsque vous écrivez l'équation `x = a + 1`, vous n'attribuez pas la valeur de `a + 1` à `x`.
Cette correspondance sert essentiellement à vérifier la forme d'une valeur, à en extraire des éléments et à traiter différemment les résultats d'une opération selon qu'elle réussit ou échoue.
Tout cela est un peu déroutant au début mais très puissant.


# Module, fonctions et clauses de garde
Dès qu'un programme dépasse quelques lignes, il devient nécessaire de le structurer.
En Elixir, les fonctions nommées doivent être écrites à l'intérieur de modules :


```elixir
defmodule BestMovie do
  def ever(name) do
    IO.puts "Le meilleur film de tous les temps est `#{name}`."
  end
end
BestMovie.ever("La cité de la peur") #=> Le meilleur film de tous les temps est `La cité de la peur`.
```



De son côté, la macro `defp` définit une fonction privée, c'est-à-dire une fonction qui ne peut être appelée qu'au sein du module qui la déclare :


```elixir
defmodule MonModule do
  def fonction_publique do
    fonction_privée()
  end

  defp fonction_privée do
    IO.puts "Je suis appelé depuis une fonction privée"
  end
end
MonModule.fonction_privée()   #=> ** (UndefinedFunctionError) function MonModule.fonction_privée/0 is undefined or private.
MonModule.fonction_publique() #=> Je suis appelé depuis une fonction privée
```



En Elixir, la correspondance de motifs s'applique aux arguments des fonctions : plusieurs peuvent porter le même nom mais le langage choisira la fonction dont les motifs correspondent aux arguments.
Au moyen du mot clé `when`, les clauses de garde permettent d'étendre cette correspondance de motifs à des vérifications plus complexes comme la distinction des types d'arguments ou un test portant sur leurs valeurs :


```elixir
defmodule GuardClause do
  def what_is(x) when is_number(x) and x > 0 do
    IO.puts "#{x} est un nombre positif"
  end

  def what_is(x) when is_list(x) do
    IO.puts "#{inspect(x)} est une liste"
  end
end
GuardClause.what_is(99)      #=> 99 est un nombre positif
GuardClause.what_is([1,2,3]) #=> [1,2,3] est une liste
```



Toutes les expressions ne sont pas autorisées dans les clauses de garde, mais [seulement quelques-unes](https://elixir.hexdocs.pm/main/patterns-and-guards.html#list-of-allowed-functions-and-operators).
Il s'agit d'un choix délibéré du langage pour garantir une exécution efficace.



# Listes et récursivité
En Elixir, une liste peut être vide ou se composer d'une tête et d'une queue. La tête (head) contient une valeur et la queue (tail) est elle-même une liste :


```elixir
iex> [ head | tail ] = [ 1, 2, 3 ]
[1, 2, 3]
iex> head
1
iex> tail
[2, 3]
```



Ainsi, une liste à un seul élément (que nous écririons habituellement sous la forme `[4]`) peut s'écrire comme la valeur 4 jointe à une liste vide, soit `[ 4 | [ ] ]`.
Cette définition permet d'aborder certains problèmes de manière récursifs et donc sans s'appuyer sur une variable de boucle et/ou les traditionnels `if` et `else` :


```elixir
defmodule MyList do
  def flip([]), do: []
  def flip([ head | tail ]) do
    flip(tail) ++ [head] # `++` permet de concaténer des listes
  end
end
MyList.flip([1, 2, 3, 4, 5]) #=> [5, 4, 3, 2, 1]
# Explication pas à pas :
#   flip([1, 2, 3, 4, 5])
#     ↳ flip([2, 3, 4, 5]) ++ [1]
#         ↳ flip([3, 4, 5]) ++ [2] ++ [1]
#             ↳ flip([4, 5]) ++ [3] ++ [2] ++ [1]
#                 ↳ flip([5]) ++ [4] ++ [3] ++ [2] ++ [1]
#                     ↳ flip([]) ++ [5] ++ [4] ++ [3] ++ [2] ++ [1]
#                         ↳ [] ++ [5] ++ [4] ++ [3] ++ [2] ++ [1]
#                             ↳ [5, 4, 3, 2, 1]
```



Note : l'usage de `++` est moins efficace qu'une version utilisant une variable pour accumuler le résultat mais cette approche reste plus didactique pour découvrir la récursion.



# L'opérateur pipe
Nous avons tous déjà croisé du code séquentiel où chaque étape dépend du résultat de la précédente :


```ruby
  cart = ShoppingCart.for_user(user_id)
  items = Inventory.check_availability(cart)
  total = Pricing.calculate_total(items, tax: 1.2)
```



L'ordre des opérations est clair, mais au final les variables intermédiaires sont là pour éviter des appels imbriqués :


```ruby
Pricing.calculate_total(
  Inventory.check_availability(
    ShoppingCart.for_user(user_id)
  ),
  tax: 1.2
)
```



Elixir propose d'écrire ce type de code d'une façon fluide et sans variable intermédiaire :


```elixir
user_id
|> ShoppingCart.for_user()
|> Inventory.check_availability()
|> Pricing.calculate_total(tax: 1.2)
```



L'opérateur `|>` prend le résultat de l'expression située précédemment et l'insère en tant que premier paramètre de l'appel de la fonction suivante.



# L'exercice sympa qui utilise tout ça (ou presque)
Maintenant que vous avez les bases, je vous propose un exercice qui m'avait bien plu lorsque j'ai découvert le langage au travers le [livre de Dave Thomas](https://pragprog.com/titles/elixir16/programming-elixir-1-6/) : trouver un nombre compris dans un intervalle donné (ex : `1..100` dans une expression ou `1..100//1` pour effectuer une correspondance de motifs, `//1` correspondant au pas).
Il existe plusieurs façon de résoudre le problème mais voici ma solution :


```elixir
defmodule Devine do
  def en_tatonnant(cible, min..max//1) when is_integer(cible) and is_integer(min) and is_integer(max) and cible > min and cible < max do
    estimation = div(min + max, 2)
    IO.puts "Est-ce #{estimation} ?"
    guess(cible, min..max, estimation)
  end
  def en_tatonnant(cible, min..max//1) when not is_integer(cible) or not is_integer(min) or not is_integer(max) or cible < min or cible > max do
    IO.puts "Très drôle…"
  end
  defp guess(cible, min.._//1, estimation) when estimation > cible do
    IO.puts "Non, c'est plus petit… Maintenant on va rechercher entre #{min} et #{estimation}"
    en_tatonnant(cible, min..estimation)
  end
  defp guess(cible, _..max//1, estimation) when estimation < cible do
    IO.puts "Non, c'est plus grand… Maintenant on va rechercher entre #{estimation} et #{max}"
    en_tatonnant(cible, estimation..max)
  end
  defp guess(cible, _, estimation) when estimation == cible do
    IO.puts "Mais oui, c'est bien #{estimation} 🤩"
  end
end
Devine.en_tatonnant(273, 1..1000)
#=> Est-ce 500 ?
#=> Non, c'est plus petit… Maintenant on va rechercher entre 1 et 500
#=> Est-ce 250 ?
#=> Non, c'est plus grand… Maintenant on va rechercher entre 250 et 500
#=> Est-ce 375 ?
#=> Non, c'est plus petit… Maintenant on va rechercher entre 250 et 375
#=> Est-ce 312 ?
#=> Non, c'est plus petit… Maintenant on va rechercher entre 250 et 312
#=> Est-ce 281 ?
#=> Non, c'est plus petit… Maintenant on va rechercher entre 250 et 281
#=> Est-ce 265 ?
#=> Non, c'est plus grand… Maintenant on va rechercher entre 265 et 281
#=> Est-ce 273 ?
#=> Mais oui, c'est bien 273 🤩
```




# Et maintenant
Vous vous doutez bien que je n'ai fait qu'effleurer le sujet mais j'ai trouvé le langage Elixir tellement singulier que je n'ai pas résisté à l'envie de vous le présenter.
Je continue ma découverte de ce langage, à mon rythme ; en ce moment, j'aborde la programmation concurrente.
À n'en pas douter, j'enchaînerai sur le [framework Phoenix](https://www.phoenixframework.org/) !
