- Préambule
- À propos
- Mise en forme
- Le guide
- Expressions
- Nommage
- Commentaires
- Modules
- Documentation
- Typespecs
- Structs
- Exceptions
- Collections
- Strings
- Expressions régulières
- Méta-programmation
- Les tests
- Ressources
- Nous aider
- Reproduction
Liquid architecture. It's like jazz — you improvise, you work together, you play off each other, you make something, they make something.
—Frank Gehry
Le style est important. Elixir peut être plein de style, mais comme pour tous les langages, il faut suivre un style de codage pour rendre son code agréable à lire.
Ceci est un guide de style de codage pour le langage de prommation Elixir. N'hésitez pas à ouvrir des pull requests et proposer des améliorations. Rejoignez la communauté Elixir !
Si vous cherchez d'autres projets auxquels contribuer, visitez le site du gestionnaire de paquets Hex (en anglais).
Ce guide est disponible dans les langues suivantes:
Elixir v1.6 a introduit les tâches Code Formatter et Mix format. Le formateur doit être privilégié pour tous les nouveaux projets et codes sources.
Les règles de cette section sont appliquées automatiquement par le formateur de code mais sont fournies ici à titre d'exemple du style conseillé.
-
Ne laissez pas d'espaces blancs inutiles en fin de ligne. [lien]
-
Chaque fichier doit se terminer par une ligne vide. [lien]
-
Utilisez des fins de ligne Unix (*pas de problème si vous utilisez BSD, Solaris, Linux ou macOS, mais faites très attention si vous utilisez Windows). [lien]
-
Si vous utilisez Git, vous pouvez ajouter le réglage suivant pour vous protéger des fins de ligne Windows qui pourraient se glisser dans votre dépôt : [lien]
git config --global core.autocrlf true
-
Utilisez des espaces autour des opérateurs et après les vigules, deux-points et point-virgules. Ne mettez pas d'espace autour des paires de parenthèses, accolades, etc. Ces espaces sont parfois inutiles à la bonne exécution de votre code, mais ils le rendent bien plus facile à lire. [lien]
sum = 1 + 2 {a, b} = {2, 3} [first | rest] = [1, 2, 3] Enum.map(["one", <<"two">>, "three"], fn num -> IO.puts(num) end)
-
Ne mettez pas d'espace après les opérateurs qui ne sont pas un mot qui ne ne prennent qu'un seul argument, ou autour de l'opérateur de gamme (
..
). [lien]0 - 1 == -1 ^pinned = some_func() 5 in 1..10
-
Placez des lignes vides à l'intérieur des
def
pour diviser vos fonctions en paragraphes logiques. [lien]def some_function(some_data) do some_data |> other_function() |> List.first() end def some_function do result end def some_other_function do another_result end def a_longer_function do one two three four end
-
Ne laissez pas de ligne vide après
defmodule
. [lien] -
Si l'en-tête de votre fonction et la clause
do:
sont trop longs pour tenir sur une seule ligne, placez la clausedo:
sur une nouvelle ligne indentée d'un niveau de plus que la ligne précédente. [lien]def some_function([:foo, :bar, :baz] = args), do: Enum.map(args, fn arg -> arg <> " is on a very long line!" end)
Lorsque la clause
do:
est sur une nouvelle ligne, traitez votre fonction comme une fonction de plusieurs lignes et séparez-la donc des autres fonctions par une ligne vide.# déconseillé def some_function([]), do: :empty def some_function(_), do: :very_long_line_here # conseillé def some_function([]), do: :empty def some_function(_), do: :very_long_line_here
-
Ajoutez une ligne vide après une affectation sur plusieurs lignes. Cela permet de signaler visuellement la fin de l'affectation. [lien]
# déconseillé some_string = "Hello" |> String.downcase() |> String.strip() another_string <> some_string # conseillé some_string = "Hello" |> String.downcase() |> String.strip() another_string <> some_string
# déconseillé aussi something = if x == 2 do "Hi" else "Bye" end String.downcase(something) # conseillé something = if x == 2 do "Hi" else "Bye" end String.downcase(something)
-
Si une list, map ou struct prend plusieurs lignes, placez chaque élément ainsi que les accolades/crochets ouvrants et fermants sur des lignes séparées. Indentez chaque élément d'un niveau, mais pas les accolades/crochets. [lien]
# déconseillé [:first_item, :second_item, :next_item, :final_item] # conseillé [ :first_item, :second_item, :next_item, :final_item ]
-
Lorsque vous déclarez une list, map ou struct, laissez le crochet ou l'accolade ouvrante sur la même ligne que le début de l'affectation. [lien]
# déconseillé list = [ :first_item, :second_item ] # conseillé list = [ :first_item, :second_item ]
-
Quand une clause
case
oucond
prend plusieurs lignes, séparez chaque cas par une ligne vide. [lien]# déconseillé case arg do true -> :ok false -> :error end # conseillé case arg do true -> :ok false -> :error end
-
Placez les commentaires au-dessus de la ligne concernée. [lien]
String.first(some_string) # déconseillé # conseillé String.first(some_string)
-
Mettez un espace entre le
#
et le texte du commentaire. [lien]#déconseillé String.first(some_string) # conseillé String.first(some_string)
-
Indentez et alignez les clauses
with
successives. Placez l'argumentdo:
sur une nouvelle ligne, aligné avec les clauses précédentes. [lien]with {:ok, foo} <- fetch(opts, :foo), {:ok, bar} <- fetch(opts, :bar), do: {:ok, foo, bar}
-
Si un
with
a un blocdo
possédant plus d'une ligne, ou a une optionelse
, utilisez la syntaxe multilignes. [lien]with {:ok, foo} <- fetch(opts, :foo), {:ok, bar} <- fetch(opts, :bar) do {:ok, foo, bar} else :error -> {:error, :bad_arg} end
-
Laissez des parenthèses, même vides, aux fonctions que vous enchaînez grâce à l'opérateur pipe (
|>
). [lien]# déconseillé some_string |> String.downcase |> String.strip # conseillé some_string |> String.downcase() |> String.strip()
-
Ne mettez pas d'espace entre le nom d'une fonction et la parenthèse ouvrante. [lien]
# déconseillé f (3 + 2) # conseillé f(3 + 2)
-
Mettez des parenthèses lorsque vous appelez une fonction, surtout dans un pipeline. [lien]
# déconseillé f 3 # conseillé f(3) # déconseillé et est interprété comme rem(2, (3 |> g)), ce qui # n'est pas ce que vous souhaitez ! 2 |> rem 3 |> g # conseillé 2 |> rem(3) |> g
-
Utilisez toujours la syntaxe spéciale pour les listes de mots-clés. [lien]
# déconseillé some_value = [{:a, "baz"}, {:b, "qux"}] # conseillé some_value = [a: "baz", b: "qux"]
-
Ne mettez pas de crochets dans les listes de mots-clés lorsqu'ils sont optionnels. [lien]
# déconseillé some_function(foo, bar, [a: "baz", b: "qux"]) # conseillé some_function(foo, bar, a: "baz", b: "qux")
Les règles dans cette section peuvent ne pas être appliquées par le formateur de code, mais elles constituent généralement les bonnes pratiques.
-
Placez les
def
d'une seule ligne correspondant à une même fonction ensemble. Mais séparez d'une ligne vide lesdef
de plusieurs lignes. [lien]def some_function(nil), do: {:error, "No Value"} def some_function([]), do: :ok def some_function([first | rest]) do some_function(rest) end
-
Si vous avez plus d'un
def
de plusieurs lignes, ne faites pas dedef
d'une seule ligne. [lien]def some_function(nil) do {:error, "No Value"} end def some_function([]) do :ok end def some_function([first | rest]) do some_function(rest) end def some_function([first | rest], opts) do some_function(rest, opts) end
-
Utilisez l'opérateur pipe (
|>
) pour enchaîner les fonctions. [lien]# déconseillé String.strip(String.downcase(some_string)) # conseillé some_string |> String.downcase() |> String.strip() # L'indentation des pipelines de plusieurs lignes doit rester # au même niveau some_string |> String.downcase() |> String.strip() # Les pipelines de plusieurs lignes à la droite d'un pattern # match doivent être indentés sur une nouvelle ligne sanitized_string = some_string |> String.downcase() |> String.strip()
Bien que ce soit la méthode conseillée, gardez en tête que copier-coller un pipeline de plusieurs lignes dans IEx peut donner une erreur de syntaxe, IEx évaluant la première ligne sans réaliser que la ligne suivante commence par un opérateur pipe. Pour éviter cela, vous pouvez mettre le code collé entre parenthèses.
-
Évitez d'utiliser l'opérateur pipe une seule fois. [lien]
# déconseillé some_string |> String.downcase() # conseillé String.downcase(some_string)
-
Utilisez une variable seule pour débuter un enchaînement de fonctions. [lien]
# déconseillé String.strip(some_string) |> String.downcase() |> String.codepoints() # conseillé some_string |> String.strip() |> String.downcase() |> String.codepoints()
-
Mettez des parenthèses lorsqu'un
def
a des arguments, et n'en mettez pas lorsqu'il n'a pas d'arguments. [lien]# déconseillé def some_function arg1, arg2 do # corps de la fonction end def some_function() do # corps de la fonction end # conseillé def some_function(arg1, arg2) do # corps de la fonction end def some_function do # corps de la fonction end
-
Utilisez
do:
pour les conditionsif
/unless
d'une seule ligne. [lien]# conseillé if some_condition, do: # quelque_chose
-
N'utilisez jamais
unless
avecelse
. Remplacez ces conditions par desif
en testant le cas positif en premier. [lien]# déconseillé unless success do IO.puts('failure') else IO.puts('success') end # conseillé if success do IO.puts('success') else IO.puts('failure') end
-
Utilisez
true
en tant que dernière condition de la clausecond
lorsque vous souhaitez intercepter toutes les valeurs dans votrecond
. [lien]# déconseillé cond do 1 + 2 == 5 -> "Nope" 1 + 3 == 5 -> "Uh, uh" :else -> "OK" end # conseillé cond do 1 + 2 == 5 -> "Nope" 1 + 3 == 5 -> "Uh, uh" true -> "OK" end
-
Utilisez des parenthèses pour les appels de fonctions sans arguments, pour les distinguer des variables. À partir d'Elixir 1.4, le compilateur signale les endroits où il existe une ambiguïté entre nom de fonction et nom de variable. [lien]
defp do_stuff, do: ... # déconseillé def my_func do # est-ce une variable ou un appel de fonction ? do_stuff end # conseillé def my_func do # il s'agit clairement d'un appel de fonction do_stuff() end
-
Utilisez la convention
snake_case
pour nommer les atomes, fonctions et variables. [lien]# déconseillé :"some atom" :SomeAtom :someAtom someVar = 5 def someFunction do ... end # conseillé :some_atom some_var = 5 def some_function do ... end
-
Utilisez la convention
CamelCase
pour nommer les modules. Laissez les accronymes comme HTTP, RFC, XML, etc. en majuscules. [lien]# déconseillé defmodule Somemodule do ... end defmodule Some_Module do ... end defmodule SomeXml do ... end # conseillé defmodule SomeModule do ... end defmodule SomeXML do ... end
-
Les noms de macros prédicats (fonctions générées à la compilation et qui renvoient un booléen) qui peuvent être utilisés entre des guards doivent posséder le préfixe
is_
. Pour une liste d'expressions autorisées, référez-vous à la documention de Guard. [lien]defmacro is_cool(var) do quote do: unquote(var) == "cool" end
-
Les noms des fonctions prédicats qui ne peuvent pas être utilisées entre des guards doivent se terminer par un point d'interrogation au lieu de commencer par le préfixe
is_
(ou similaire). [lien]def cool?(var) do # Complex check if var is cool not possible in a pure function. end
-
Les fonctions private ayant le même nom que des fonctions public doivent commencer par
do_
. [lien]def sum(list), do: do_sum(list, 0) # fonctions private defp do_sum([], total), do: total defp do_sum([head | tail], total), do: do_sum(tail, head + total)
-
Écrivez du code clair dont on comprend l'objectif en lisant son flux de contrôle, sa structure, et les différents noms (fonctions, variables...). [lien]
-
Les commentaires plus longs qu'un mot commencent par une majuscule. Utilisez de la ponctuation si vous faites des phrases complètes. Mettez un espace après chaque point. [lien]
# déconseillé # ce commentaire devrait commencer par une majuscule # conseillé # Example de commentaire # Utilisez de la ponctuation pour les phrases complètes.
-
Les annotations devraient généralement être écrites sur la ligne juste au-dessus du code concerné. [lien]
-
Les mots-clés d'annotation doivent être en majuscules et suivis de deux-points (
:
) et d'un espace, puis d'une note décrivant le problème. [lien]# TODO: Deprecate in v1.5. def some_function(arg), do: {:ok, arg}
-
Dans les cas où le problème est si évident qu'une documentation serait redondante, il est possible de laisser une annotation seule, sans note. Cette utilisation des annotations doit être une exception, pas une règle ! [lien]
start_task() # FIXME Process.sleep(5000)
-
Utilisez
TODO
pour signaler du code auquel des fonctionnalités devront être ajoutées. [lien] -
Utilisez
FIXME
pour signaler du code qui doit être corrigé. [lien] -
Utilisez
OPTIMIZE
pour signaler du code lent ou inefficace qui pourrait créer des problèmes de performance. [lien] -
Utilisez
HACK
pour signaler du code issu de pratiques de programmation douteuses et qui devrait être refactorisé. [lien] -
Utilisez
REVIEW
pour signaler des éléments qui doivent être vérifiés pour confirmer qu'ils fonctionnent comme souhaité. Par exemple :REVIEW: Are we sure this is how the client does X currently?
[lien] -
Utilisez d'autres mots-clés personnalisés si vous pensez que cela est utile. N'oubliez pas d'écrire une documentation sur ces mots-clés personnalisés dans le
README
(ou équivalent) de votre projet. [lien]
-
Un seul module dans un seul fichier. Sauf si un module est utilisé uniquement à l'intérieur d'un autre module (un test, par exemple). [lien]
-
Utilisez la convention
snake_case
pour les noms de fichers et la conventionCamelCase
pour les noms de modules. [lien]# dans un fichier appelé some_module.ex defmodule SomeModule do end
-
Representez chaque niveau d'imbrication dans un nom de module par des dossiers correspondants. [lien]
# le code suivant se trouverait dans ce fichier: parser/core/xml_parser.ex defmodule Parser.Core.XMLParser do end
-
Listez les attributs et directives des modules dans cet ordre : [lien]
@moduledoc
@behaviour
use
import
alias
require
defstruct
@type
@module_attribute
@callback
@macrocallback
@optional_callbacks
Ajoutez une ligne vide entre chaque groupe, et trier les termes par ordre alphabétique (comme les noms de modules). Voici un exemple montrant globalement comment organiser vos modules :
defmodule MyModule do @moduledoc """ An example module """ @behaviour MyBehaviour use GenServer import Something import SomethingElse alias My.Long.Module.Name alias My.Other.Module.Example require Integer defstruct name: nil, params: [] @type params :: [{binary, binary}] @module_attribute :foo @other_attribute 100 @callback some_function(term) :: :ok | {:error, term} @macrocallback macro_name(term) :: Macro.t() @optional_callbacks macro_name: 1 ... end
-
Utilisez la pseudo-variable
__MODULE__
lorsqu'un module doit référer à soi-même. Si ce module doit changer de nom, cela évite de devoir le mettre à jour à plusieurs endroits. [lien]defmodule SomeProject.SomeModule do defstruct [:name] def name(%__MODULE__{name: name}), do: name end
-
Si vous voulez un nom plus joli que
__MODULE__
, vous pouvez créer un alias. [lien]defmodule SomeProject.SomeModule do alias __MODULE__, as: SomeModule defstruct [:name] def name(%SomeModule{name: name}), do: name end
-
Évitez les répétitions dans les noms de modules et namespaces. Cela améliore la lisibilité du code et permet d'éviter les conflits d'alias (anglais). [lien]
# déconseillé defmodule Todo.Todo do ... end # conseillé defmodule Todo.Item do ... end
La documentation en Elixir (lue dans iex
avec h
, ou générée avec ExDoc)
utilise les attributs de module @moduledoc
et @doc
.
-
Dans vos modules, incluez toujours l'attribut
@moduledoc
sur la ligne juste aprèsdefmodule
. [lien]# déconseillé defmodule AnotherModule do use SomeModule @moduledoc """ About the module """ ... end # conseillé defmodule AThirdModule do @moduledoc """ About the module """ use SomeModule ... end
-
Utilisez
@moduledoc false
si vous n'avez pas l'intention de documenter le module. [lien]defmodule SomeModule do @moduledoc false ... end
-
Séparez
@moduledoc
du reste du code par une ligne vide. [lien]# déconseillé defmodule SomeModule do @moduledoc """ About the module """ use AnotherModule end # conseillé defmodule SomeModule do @moduledoc """ About the module """ use AnotherModule end
-
Utilisez heredocs avec markdown pour la documentation. [lien]
# déconseillé defmodule SomeModule do @moduledoc "About the module" end defmodule SomeModule do @moduledoc """ About the module Examples: iex> SomeModule.some_function :result """ end # conseillé defmodule SomeModule do @moduledoc """ About the module ## Examples iex> SomeModule.some_function :result """ end
Les typespecs sont des notations pour déclarer des types et spécifications, ces dernières sont utilisées pour documenter ou pour l'outil d'analyse statique Dialyzer.
Les types personnalisés doivent être définis en haut des modules avec les autres directives (voir le chapitre sur les modules).
-
Placez les définitions de
@typedoc
and@type
ensembles, en séparant chaque paire d'une ligne vide. [lien]defmodule SomeModule do @moduledoc false @typedoc "The name" @type name :: atom @typedoc "The result" @type result :: {:ok, term} | {:error, term} ... end
-
Si une union de type est trop longue pour tenir sur une seule ligne, placez chaque morceau du type sur une ligne séparée, en indentant d'un niveau après le nom du type. [lien]
# déconseillé @type long_union_type :: some_type | another_type | some_other_type | one_more_type | a_final_type # conseillé @type long_union_type :: some_type | another_type | some_other_type | one_more_type | a_final_type
-
Nommer le type principal d'un module
t
. Voici par exemple la spécification de type pour une struct: [lien]defstruct name: nil, params: [] @type t :: %__MODULE__{ name: String.t() | nil, params: Keyword.t() }
-
Placez les spécifications juste avant la définition de fonction, sans les séparer d'une ligne vide. [lien]
@spec some_function(term) :: result def some_function(some_data) do {:ok, some_data} end
-
Utilisez une liste d'atomes pour les struct qui ont des champs ayant
nil
comme valeur par défaut. [lien]# déconseillé defstruct name: nil, params: nil, active: true # conseillé defstruct [:name, :params, active: true]
-
Ne mettez pas de crochets lorsque l'argument d'une
defstruct
est une liste de mots-clés. [lien]# déconseillé defstruct [params: [], active: true] # conseillé defstruct params: [], active: true # obligatoire - les crochets ne sont pas optionnels quand # il y a au moins un atome dans la liste defstruct [:name, params: [], active: true]
-
Si la déclaration d'une struct prends plusieurs lignes, placez chaque élément sur une ligne différente et indentez-les de sorte à ce qu'ils soient alignés. [lien]
defstruct foo: "test", bar: true, baz: false, qux: false, quux: 1
Si une struct de plusieurs lignes nécessite des crochets, formattez-la comme une liste de plusieurs lignes.
defstruct [ :name, params: [], active: true ]
-
Terminez vos noms d'exceptions par
Error
. [lien]# déconseillé defmodule BadHTTPCode do defexception [:message] end defmodule BadHTTPCodeException do defexception [:message] end # conseillé defmodule BadHTTPCodeError do defexception [:message] end
-
Utilisez des messages d'erreurs composés uniquement de minuscules. [lien]
# déconseillé raise ArgumentError, "This is not valid." # conseillé raise ArgumentError, "this is not valid"
Il n'y a pas encore de guide pour les collections.
-
Comparez des chaînes de caractères avec l'opérateur de concaténation plutôt que les modèles binaires. [lien]
# déconseillé <<"my"::utf8, _rest::bytes>> = "my string" # conseillé "my" <> _rest = "my string"
Il n'y a pas encore de guide pour les expressions régulières.
- Évitez la méta-programmation inutile. [lien]
-
Lorsque vous écrivez des assertions ExUnit, soyez consistant dans l'ordre entre la valeur attendue et la valeur testée. Préférez placer le résultat attendu sur la droite, sauf si l'assertion est un pattern match. [lien]
# conseillé - résultat attendu sur la droite assert actual_function(1) == true assert actual_function(2) == false # déconseillé - ordre inconsistant assert actual_function(1) == true assert false == actual_function(2) # obligatoire - si l'assertion est un pattern match assert {:ok, expected} = actual_function(3)
-
Aleksei Magusev's Elixir Style Guide (en anglais) — Un guide de style Elixir bien ancré, issu du style de codage utilisé dans les bibliothèques de base d'Elixir. Développé par Aleksei Magusev et Andrea Leopardi, membres de l'équipe Elixir. Bien que le projet Elixir ne respecte aucun guide de style spécifique, il s'agit du guide le plus proche de ses conventions.
-
Credo's Elixir Style Guide (en anglais) — Guide de style Elixir, réalisé par Credo, outil d'analyse statique de code.
Référez-vous au dépôt Awesome Elixir pour des bibliothèques et outils qui vous aideront à contrôler la qualité de votre code et de votre style de codage.
Nous avons l'espoir que ce dépôt devienne un point de rencontre pour discuter des meilleurs pratiques de programmation Elixir. N'hésitez pas à ouvrir des tickets ou à ouvrir des pull requests pour améliorer ce guide. Merci d'avance pour votre aide !
Lisez les indications sur comment contribuer (en anglais) pour plus d'informations.
Un guide de style communautaire n'a pas d'intérêt sans l'aide de la communauté. N'hésitez pas à laisser une star à ce dépôt pour le faire connaître. Et partagez ce guide avec tous les programmeurs Elixir que vous connaissez, pour qu'ils puissent y contribuer.
Ce guide est protégé par la licence
Creative Commons Attribution 3.0 Unported License
La structure de ce guide, des bribes de code d'exemple, ainsi que de nombreux points initiaux de ce guide ont été empreintés au Guide de style Ruby. Beaucoup de choses étaient aussi applicables à Elixir et nous on permis de sortir ce guide au plus vite.
Voici la liste des personnes ayant généreusement contribué à ce guide.