Dans une data
déclaration, un constructeur de type est la chose sur le côté gauche du signe égal. Le (s) constructeur (s) de données sont les éléments du côté droit du signe égal. Vous utilisez des constructeurs de type là où un type est attendu et vous utilisez des constructeurs de données là où une valeur est attendue.
Constructeurs de données
Pour simplifier les choses, nous pouvons commencer par un exemple de type qui représente une couleur.
data Colour = Red | Green | Blue
Ici, nous avons trois constructeurs de données. Colour
est un type et Green
est un constructeur qui contient une valeur de type Colour
. De même, Red
et Blue
sont tous deux des constructeurs qui construisent des valeurs de type Colour
. Nous pourrions imaginer le pimenter cependant!
data Colour = RGB Int Int Int
Nous n'avons toujours que le type Colour
, mais ce RGB
n'est pas une valeur - c'est une fonction prenant trois Ints et renvoyant une valeur! RGB
a le type
RGB :: Int -> Int -> Int -> Colour
RGB
est un constructeur de données qui est une fonction prenant certaines valeurs comme arguments, puis les utilise pour construire une nouvelle valeur. Si vous avez effectué une programmation orientée objet, vous devez le reconnaître. En POO, les constructeurs prennent également certaines valeurs comme arguments et renvoient une nouvelle valeur!
Dans ce cas, si nous appliquons RGB
à trois valeurs, nous obtenons une valeur de couleur!
Prelude> RGB 12 92 27
#0c5c1b
Nous avons construit une valeur de type Colour
en appliquant le constructeur de données. Un constructeur de données contient une valeur comme le ferait une variable ou prend d'autres valeurs comme argument et crée une nouvelle valeur . Si vous avez déjà fait de la programmation, ce concept ne devrait pas vous être très étrange.
Entracte
Si vous voulez construire un arbre binaire pour stocker String
s, vous pouvez imaginer faire quelque chose comme
data SBTree = Leaf String
| Branch String SBTree SBTree
Ce que nous voyons ici est un type SBTree
qui contient deux constructeurs de données. En d'autres termes, il existe deux fonctions (à savoir Leaf
et Branch
) qui construiront des valeurs de SBTree
type. Si vous n'êtes pas familier avec le fonctionnement des arbres binaires, accrochez-vous simplement. Vous n'avez pas vraiment besoin de savoir comment fonctionnent les arbres binaires, seulement que celui-ci stocke les String
s d'une manière ou d'une autre.
Nous voyons également que les deux constructeurs de données prennent un String
argument - c'est la chaîne qu'ils vont stocker dans l'arborescence.
Mais! Et si nous voulions également pouvoir stocker Bool
, nous devions créer un nouvel arbre binaire. Cela pourrait ressembler à ceci:
data BBTree = Leaf Bool
| Branch Bool BBTree BBTree
Constructeurs de type
Les deux SBTree
et BBTree
sont des constructeurs de type. Mais il y a un problème flagrant. Voyez-vous à quel point ils sont similaires? C'est le signe que vous voulez vraiment un paramètre quelque part.
Nous pouvons donc faire ceci:
data BTree a = Leaf a
| Branch a (BTree a) (BTree a)
Maintenant, nous introduisons une variable de type en a
tant que paramètre du constructeur de type. Dans cette déclaration, BTree
est devenue une fonction. Il prend un type comme argument et renvoie un nouveau type .
Il est important ici de considérer la différence entre un type concret (les exemples incluent Int
, [Char]
et Maybe Bool
) qui est un type qui peut être attribué à une valeur dans votre programme, et une fonction de constructeur de type dont vous avez besoin pour alimenter un type pour pouvoir être assigné à une valeur. Une valeur ne peut jamais être de type "liste", car elle doit être une "liste de quelque chose ". Dans le même esprit, une valeur ne peut jamais être de type "arbre binaire", car elle doit être un "arbre binaire stockant quelque chose ".
Si nous passons, disons, Bool
comme argument à BTree
, il renvoie le type BTree Bool
, qui est un arbre binaire qui stocke Bool
s. Remplacez chaque occurrence de la variable de type a
par le type Bool
, et vous pourrez voir par vous-même comment c'est vrai.
Si vous le souhaitez, vous pouvez voir BTree
comme une fonction avec le genre
BTree :: * -> *
Les types sont un peu comme des types - le *
indique un type concret, donc nous disons qu'il BTree
va d'un type concret à un type concret.
Emballer
Revenez ici un moment et prenez note des similitudes.
Un constructeur de données est une «fonction» qui prend 0 ou plusieurs valeurs et vous renvoie une nouvelle valeur.
Un constructeur de type est une "fonction" qui prend 0 ou plusieurs types et vous redonne un nouveau type.
Les constructeurs de données avec des paramètres sont sympas si nous voulons de légères variations dans nos valeurs - nous mettons ces variations dans les paramètres et laissons le créateur de la valeur décider quels arguments ils vont mettre. Dans le même sens, les constructeurs de types avec des paramètres sont cool si nous voulons de légères variations dans nos types! Nous mettons ces variations comme paramètres et laissons le type qui crée le type décider des arguments qu'il va mettre.
Une étude de cas
Comme la dernière ligne droite ici, nous pouvons considérer le Maybe a
type. Sa définition est
data Maybe a = Nothing
| Just a
Voici Maybe
un constructeur de type qui renvoie un type concret. Just
est un constructeur de données qui renvoie une valeur. Nothing
est un constructeur de données qui contient une valeur. Si nous regardons le type de Just
, nous voyons que
Just :: a -> Maybe a
En d'autres termes, Just
prend une valeur de type a
et renvoie une valeur de type Maybe a
. Si nous regardons le genre de Maybe
, nous voyons que
Maybe :: * -> *
En d'autres termes, Maybe
prend un type concret et renvoie un type concret.
Encore une fois! La différence entre un type concret et une fonction constructeur de type. Vous ne pouvez pas créer une liste de Maybe
s - si vous essayez d'exécuter
[] :: [Maybe]
vous obtiendrez une erreur. Vous pouvez cependant créer une liste de Maybe Int
, ou Maybe a
. C'est parce que Maybe
c'est une fonction de constructeur de type, mais une liste doit contenir des valeurs d'un type concret. Maybe Int
et Maybe a
sont des types concrets (ou si vous le souhaitez, des appels à des fonctions de constructeur de type qui renvoient des types concrets.)
Car
s'agit à la fois d'un constructeur de type (sur le côté gauche du=
) et d'un constructeur de données (sur le côté droit). Dans le premier exemple, leCar
constructeur de type n'accepte aucun argument, dans le second, il en prend trois. Dans les deux exemples, leCar
constructeur de données prend trois arguments (mais les types de ces arguments sont dans un cas fixes et dans l'autre paramétrés).