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]