Ty

Typespecs en Elixir

1 exercice

À propos de Typespecs

Elixir est un langage à typage dynamique, ce qui signifie qu'il ne vérifie pas les types à la compilation. Les spécifications de type restent toutefois utiles, car elles :

  • Servent de documentation.
  • Peuvent être exploitées par des outils d'analyse statique comme Dialyzer pour repérer d'éventuels bugs.

On peut ajouter une spécification de type à une fonction à l'aide de l'attribut de module @spec, juste avant la définition de la fonction.

@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length

Types

Expressions autorisées dans une spécification de type :

  • Types de base, par exemple :

    • boolean()
    • integer(), non_neg_integer(), pos_integer(), float()
    • list()
    • map()
    • any()
  • Une union de types :

    • par exemple integer() | list(integer())
  • Des types paramétrés :

    • par exemple list(integer()), une liste d'entiers
    • par exemple %{age: integer()}, une map avec une valeur de type entier sous la clé :age
  • Des types distants (définis dans un module), par exemple :

  • Des littéraux, par exemple :

    • %{}, une map vide
    • [], une liste vide (mais [any()] est une liste non vide)
    • par exemple :ok, un atome
  • Des types spécialisés intégrés, par exemple :

    • char(), un entier dans l'intervalle 0..0x10FFFF
    • charlist(), une liste de caractères
    • keyword(), une liste de tuples de deux éléments, dont le premier élément est un atome
  • Des types personnalisés

La liste complète de tous les types se trouve dans la section « Typespecs » de la documentation officielle.

Nommer les arguments

Il est également possible de nommer les arguments dans la spécification de type, ce qui est utile pour distinguer plusieurs arguments de même type.

@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()

Types personnalisés

On peut définir des types personnalisés à l'aide de l'un des trois attributs de module :

  • @type : définit un type public
  • @typep : définit un type privé
  • @opaque : définit un type public dont la structure est privée
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}

@spec to_hex(color()) :: String.t()

String.t() vs binary() vs string()

String.t() est le type correct à utiliser pour les strings Elixir, qui sont des binaires encodés en UTF-8. Techniquement, String.t() est défini comme un binary(), et ces deux types sont équivalents pour les outils d'analyse, mais String.t() est le choix le plus révélateur de l'intention pour documenter des fonctions qui manipulent des strings Elixir.

En revanche, string() est un type différent. C'est une string Erlang, appelée charlist en Elixir. Il faut éviter le type string() dans les spécifications de type et utiliser charlist() à la place.

Dialyzer

Dialyzer est un outil d'analyse statique qui peut détecter des problèmes tels que des erreurs de type dans du code Erlang et Elixir. La façon la plus simple d'utiliser Dialyzer dans un projet Elixir est d'avoir recours à Dialyxir.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet

Apprends Typespecs