// BLOG
Elm #5 - Types
Elm est un langage avec un typage statique. Cela signifie que les types des données sont connus à l’avance et que le compilateur vérifie que les valeurs correspondent bien aux types annoncés.
Elm est un langage avec un typage statique. Cela signifie que les types des données sont connus à l’avance et que le compilateur vérifie que les valeurs correspondent bien aux types annoncés.
Les types primitifs de Elm
Les types primitifs de Elm sont les types disponibles par défaut dans le langage Elm.
Il s’agit de Char
, String
, Bool
et number
( Int & Float ).
-- Char
'a'
-- String
"a"
"Hello"
"""
This is
a multiligne
String
"""
-- number
5
-- Float
3.5
-- Bool
True
Flase
Types Variables
Comme nous l’avons vue dans le chapitre “fonctions”, les Types Variables sont des types génériques qui peuvent être remplacés par n’importe quel types.
Ils doivent être écrit en minuscules.
On utilise souvent a, b, c … mais rien ne nous empêche d’utiliser des mots plus explicites : valeurs, chat …
Exemple avec la fonction List.reverse
:
List.reverse : List a -> List a
-- On peut utiliser la fonction List.reverse avec des String, des Int ...
-- List String -> List String
-- List Int -> List Int
La fonction List.reverse
accepte n’importe quel type à la place de “a” mais d’après sa signature si par exemple on passe un String en paramètre, le resultat doit être un String, idem si c’est un Int, le résultat doit être un Int.
Types Variables contraints
Il s’agit des types number
, appendable
, compappend
, comparable
.
Ils peuvent prendre différents types mais parmi un choix restreint par Elm :
number = Int or Float
appendable = String or List a
compappend = String or List comparable
comparable = Int, Float, Char, String, lists and tuples of comparable values
Par exemple :
negate : number -> number
-- number ne peut être que un type Float ou un type Int
Type Maybe
Elm interdit la valeur null (ou undefined…) et a prévu un Union Type Maybe
pour pallier ce problème.
Le type Maybe
est définie avec deux balises : Just
et Nothing
qui doivent être écrit avec une majuscule. La valeur d’un type Maybe
ne peut être que l’un des deux.
type Maybe a = Just a | Nothing
Une valeur de type Maybe
peut avoir juste une valeur a
(c’est à dire de n’importe quel type) soit n’a aucune valeur, c’est à dire “Rien”.
On l’utilise pour les fonctions qui sont susceptibles de ne pas renvoyer de valeur. Ainsi implémenté, une fonction ne provoquera pas d’erreur mais renvera soit une valeur de n’importe que type soit Nothing.
C’est le cas par exemple de la fonction get
:
-- signature de la fonction "get" :
get : Int -> Array a -> Maybe a
Exemple d’utilisation de Maybe
:
helloWorld : Maybe String -> String
helloWorld name =
case name of
Nothing ->
"Hello, World!"
Just aName ->
"Hello, " ++ aName ++ "!"
Le complilateur nous oblige à traiter les cas d’erreurs.
Maybe est définie dans le module Maybe
qui fourni plusieurs fonctions.
Types Aliases
Un alias de type est un nom personnalisé donné à un autre type existant. Cela permet une lecture plus “friendly” du code.
type alias Name = String
-- "Name" devient un type personnalisé de type String
-- Un type alias permet de raccourcir un autre type :
type alias Pet =
{ name: String
, age: Int
}
-- "Pet" devient un type personnalisé de type record
C’est un cas fréquent en Elm de donner un nom personnalisé à une structure de données de type record
afin de raccourcir son écriture. C’est plus pratique si on doit utiliser ce record
à plusieurs reprises dans son code.
Autre exemple :
type alias User =
{ name : String
, bio : String
}
hasDecentBio : User -> Bool
hasDecentBio user =
String.length user.bio > 80
-- C'est plus court et plus lisible que d'écrire :
hasDecentBio : { name : String, bio : String } -> Bool
hasDecentBio user =
String.length .bio > 80
On peut utiliser un type alias en tant que constructeur.
-- Création d'une variable de type "Pet" (qui est un record) et affectation des ses deux valeurs "name" et "age" :
dog = Pet "Puppy" 2
-- { name = "Puppy", age = 2 } : Pet
Union Types
Un “union type” est un type personnalisé constitué de valeurs prédéfinies.
type Answer = Yes | No
-- Le type Answer peut avoir uniquement l'état "Yes" ou "No"
respond : Answer -> String
respond answer =
case answer of
Yes ->
...
No ->
...
-- La fonction "answer" prend en paramètre uniquement une des valeurs de l'union type "Answer" donc soit "Yes" soit "No"
Associer de l'information (Payload)
Les types d’union peuvent être associées à de l’information :
type Answer = Yes | No | Other String
Dans ce cas “Other” devra avoir une valeur String associée :
-- Les parenthèses sont nécessaires sinon Elm interprète qu'il s'agit de deux paramètres
respond (Other "Hello")
Autre exemple :
type Answer = Message Int String
-- Création d'une instance Message
Message 1 "Hello"
Imbrication
On peut imbriquer des unions types :
type OtherAnswer = DontKnow | Perhaps | Undecided
-- On imbrique l'union type "OtherAnswer" dans l'union type "Answer"
type Answer = Yes | No | Other OtherAnswer
-- Ici Other ne pourra prendre que les valeurs "DontKnow", "Perhaps" ou "Undecided"
respond (Other Perhaps)
Constructeur de fonctions
Comme tout est fonction daans Elm, les types d’union se comportent également comme des fonctions. Par exemple avec ce type :
-- création d'un union type "Answer"
type Answer = Message Int String
On créer un tag Message de cette manière :
Message 1 "Hello"
Il est aussi possible de faire de l’application partielle, comme avec n’importe quelle autre fonction. On appelle cela des constructeurs car vous pouvez vous en servir pour construire des types complets. Par exemple utiliser Message comme fonction pour construire (Message 1 “Hello”).
Variables de type
Il est aussi possible d’utiliser des variables de type (aussi appelées stand-in) dans la déclaration d’un union type :
type Answer a = Yes | No | Other a
C’est un Answer qui peut être utilisé avec différents types, comme Int ou String.
Par exemple, respond
pourrait ressembler à ça :
respond : Answer Int -> String
respond answer =
...
Ici nous spécifions que le stand-in “a” devrait être de type Int en utilisant la signature Answer Int.
Nous serons ensuite capable d’appeler respond comme ceci :
respond (Other 123)
Mais respond (Other “Hello”) échouera car respond
s’attend à recevoir un entier en lieu et place de a. Les parenthèses sont nécessaires pour que “Other 123” ne soit pas confondue avec des paramètres.
Cas d'usage
Dans Elm, les Unions Types sont fréquement utilisés avec une instruction case of
qui est assez similaire aux switch case
dans d’autres langages. On peut ainsi tester des valeurs et effectuer des actions en conséquence.
Par exemple dans les fonctions Update
des applications Elm, on traite les messages provenant de la View
et on effectue des actions selon le message reçu pour modifier le Model
.