Fonctions : la suite

Les variables de type

Considérez une fonction avec la signature suivante :

indexOf : String -> List String -> Int

Cette fonction hypothétique prend une chaîne de caractères et une liste de chaînes de caractères en paramètre et retourne l'index où la chaîne de caractère donnée à été trouvée dans la liste, ou -1 si la chaîne n'a pas été trouvée.

Mais que se passe-t-il si à la place, nous avions une liste d'entiers ? Nous ne pourrions pas utiliser cette fonction. En revanche, nous pouvons rendre cette fonction générique en utilisant des variables de types aussi appelées stand-ins à la place des types spécifiques des variables.

indexOf : a -> List a -> Int

En remplaçant String par a, la signature indique maintenant que indexOf prend une valeur de n'importe quel type a et une liste du même type a et retourne un entier. À partir du moment où les types correspondent, le compilateur sera heureux. Vous pouvez appeler indexOf avec un String et une liste de String, ou un Int et une liste de Int, ça marchera dans les deux cas.

De cette façon, les fonctions peuvent être rendues plus génériques. Il est aussi possible d'avoir plusieurs variables de type :

switch : ( a, b ) -> ( b, a )
switch ( x, y ) =
  ( y, x )

Cette fonction prend en paramètre un tuple de type a, b et retourne un tuple de type b, a. Tous ces appels sont valides :

switch (1, 2)
switch ("A", 2)
switch (1, ["B"])

Remarque : n'importe quel nom de variable en minuscules peut être utilisé pour une variable de type, a et b ne sont que des conventions. Par exemple, la signature ci-dessous est parfaitement valable :

indexOf : thing -> List thing -> Int

Fonctions en tant que paramètres

Considérons la signature suivante :

map : (Int -> String) -> List Int -> List String

Cette fonction :

  • prend en argument une fonction : la partie (Int -> String)
  • une liste d'entiers
  • et retourne une liste de chaînes de caractères.

La partie intéressante est celle concernant le (Int -> String). Cela signifie qu'une fonction respectant la signature (Int -> String) doit être fournie.

Par exemple, la fonction toString fournie par Elm est une bonne candidate. Vous pourriez alors appeler la fonction map de cette manière :

map toString [1, 2, 3]

Mais Int et String sont trop spécifiques. C'est pourquoi, la plupart du temps, vous verrez des signatures utilisant des variables de types à la place :

map : (a -> b) -> List a -> List b

Cette fonction transforme une liste de a en liste de b. Nous nous fichons de ce que a et b représentent, à partir du moment où la fonction passée comme premier argument utilise ces mêmes types.

Imaginons que nous ayons des fonctions avec ces signatures :

convertStringToInt : String -> Int
convertIntToString : Int -> String
convertBoolToInt : Bool -> Int

Nous pourrions alors appeler notre map générique de cette manière :

map convertStringToInt ["Hello", "1"]
map convertIntToString [1, 2]
map convertBoolToInt [True, False]

results matching ""

    No results matching ""