133 votes

Type Haskell vs Constructeur de données

Donc, je suis en train d'apprendre Haskell de learnyouahaskell.com et je vais avoir de la difficulté à comprendre le type de constructeurs et constructeurs de données. Par exemple, je ne comprends pas vraiment la différence entre ce:

data Car = Car { company :: String  
               , model :: String  
               , year :: Int  
               } deriving (Show) 

et ceci:

data Car a b c = Car { company :: a  
                     , model :: b  
                     , year :: c   
                     } deriving (Show)  

Je comprends que le premier est tout simplement à l'aide d'un constructeur ("Voiture") à construire des données de type Voiture. Je ne comprends pas vraiment le second.

Aussi, comment faire pour les types de données définis comme ceci:

data Color = Blue | Green | Red

dans tout cela? Ce que je comprends, le troisième exemple (Couleur) est un type qui peut être dans trois états: Bleu, Vert ou Rouge. Mais qui est en conflit avec la façon dont je comprends les 2 premiers exemples: est-ce que le type de Voiture ne peut être dans un état, "Voiture", ce qui peut prendre différents paramètres de construire? Si oui, comment le deuxième exemple?

Essentiellement, je suis à la recherche d'une explication qui unifie les trois ci-dessus des exemples de code/constructions.

Si j'ai besoin de spécifier plus sur ce que je ne comprends pas, dis-le moi dans les commentaires.

249voto

kqr Points 6147

En data déclaration, qui est un constructeur de type est la chose sur le côté gauche du signe égal. Les données constructeur(s) sont les choses sur la droite du signe égal. Vous utilisez le type des constructeurs où un type est prévu, et que vous utilisez des données constructeurs où une valeur est attendue.

Les données constructeurs

Pour simplifier les choses, nous pouvons commencer avec un exemple d'un 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 les deux constructeurs qui construisent les valeurs de type Colour. Nous pourrions imaginer d'épice il bien!

data Colour = RGB Int Int Int

Nous avons encore juste le type Colour, mais RGB n'est pas une valeur, c'est une fonction prenant trois ints et de retourner une valeur! RGB a le type

RGB :: Int -> Int -> Int -> Colour

RGB données constructeur qui est une fonction prenant certaines valeurs comme ses arguments, puis les utilise pour construire une nouvelle valeur. Si vous avez fait toute la programmation orientée objet, vous devez le reconnaître. En programmation orientée objet, les constructeurs de prendre certaines valeurs comme arguments et retourne une nouvelle valeur!

Dans ce cas, si l'on applique 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 les données constructeur. Données constructeur contient une valeur comme une variable, ou prend d'autres valeurs en argument et qui crée une nouvelle valeur. Si vous avez fait de la programmation précédente, ce concept ne devrait pas être très étrange.

Entracte

Si vous voulez construire un arbre binaire pour stocker Strings, 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 permettra de construire des valeurs de l' SBTree type. Si vous n'êtes pas familier avec la façon d'arbres binaires de travail, tout y accrocher. Vous n'avez pas réellement besoin de savoir comment les arbres binaires de travail, ce que c'est que l'on stocke Strings, d'une certaine façon.

Nous voyons aussi que les deux constructeurs de données prennent un String de l'argument – c'est la Chaîne qu'ils vont conserver dans l'arbre.

Mais! Que faire si nous voulions aussi être en mesure de stocker Bool, il faudrait créer un nouvel arbre binaire. Il pourrait ressembler à quelque chose comme ceci:

data BBTree = Leaf Bool
            | Branch Bool BBTree BBTree

Type de constructeurs

Les deux SBTree et BBTree sont de type des constructeurs. Mais il y a un problème flagrant. Voyez-vous comment ils se ressemblent? C'est un signe que vous voulez vraiment un paramètre quelque part.

Donc, nous pouvons faire ceci:

data BTree a = Leaf a
             | Branch a (BTree a) (BTree a)

Maintenant, nous introduisons une variable de type a en tant que paramètre au constructeur de type. Dans cette déclaration, BTree est devenu une fonction. Il faut un type comme argument et retourne un nouveau type.

Il est important ici de considérer la différence entre un type de béton (les exemples incluent Int, [Char] et Maybe Bool) qui est un type qui peut être attribué à une valeur dans votre programme, et un constructeur de type fonction dont vous avez besoin pour nourrir un type de pouvoir être attribué à une valeur. Une valeur ne peut jamais être de type "liste", parce qu'elle doit être une "liste de quelque chose". Dans le même esprit, une valeur ne peut jamais être de type "arbre binaire", parce qu'il doit être un "arbre binaire de stocker quelque chose".

Si nous passons, disons, Bool comme un argument à l' BTree, il retourne le type BTree Bool, ce qui est un arbre binaire qui stocke Bools. Remplacer chaque occurrence de la variable de type a avec le type Bool, et vous pouvez voir par vous-même combien c'est vrai.

Si vous le souhaitez, vous pouvez visualiser BTree comme une fonction du genre

BTree :: * -> *

Les types sont un peu comme les types – * indique un type de béton, de sorte que nous disons BTree est à partir d'un béton de type pour un type de béton.

L'emballage jusqu'à

L'étape de retour ici un moment et de prendre note des similitudes.

  • Une des données constructeur est une "fonction" qui prend 0 ou plusieurs valeurs et vous donne en retour une nouvelle valeur.

  • Un constructeur de type est une "fonction" qui prend 0 ou plusieurs types et vous donne un nouveau type.

Les données constructeurs avec paramètres sont cool si nous voulons que de légères variations dans nos valeurs, nous avons mis ces variations des paramètres et de laisser le gars qui crée la valeur de décider quels sont les arguments qu'ils vont mettre en. Dans le même sens, le type de constructeurs avec paramètres sont cool si nous voulons que de légères variations dans nos types! Nous avons mis ces variations de paramètres et de laisser le gars qui a crée le type décider quels sont les arguments qu'ils vont mettre en.

Une étude de cas

Comme le tronçon de la maison ici, on peut considérer l' Maybe a type. C'est la définition est

data Maybe a = Nothing
             | Just a

Ici, Maybe est un constructeur de type, qui retourne un type concret. Just données constructeur qui retourne une valeur. Nothing données constructeur qui contient une valeur. Si l'on regarde le type d' 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 l'on regarde le type d' Maybe,, nous voyons que

Maybe :: * -> *

En d'autres termes, Maybe prend un type de béton et renvoie un type concret.

Une fois de plus! La différence entre un type de béton et un constructeur de type fonction. Vous ne pouvez pas créer une liste d' Maybes. Si vous essayez d'exécuter

[] :: [Maybe]

vous recevrez un message d'erreur. Vous pouvez toutefois créer une liste d' Maybe Intou Maybe a. C'est parce qu' Maybe est un constructeur de type fonction, mais une liste doit contenir des valeurs d'un type concret. Maybe Int et Maybe a sont des types de béton (ou si vous le souhaitez, des appels au constructeur de type des fonctions qui retournent des types de béton.)

47voto

MathematicalOrchid Points 15354

Haskell a des types de données algébriques, que très peu d'autres langues. C'est peut-être ce qui est source de confusion.

Dans d'autres langues, vous pouvez généralement faire un "record", "struct" ou similaire, qui a un tas de champs nommés qui détiennent des différents types de données. Vous pouvez aussi parfois faire une "énumération", qui a une (petite) série de fixe valeurs possibles (par exemple, votre Red, Green et Blue).

En Haskell, vous pouvez combiner les deux en même temps. Bizarre, mais vrai!

Pourquoi est-il appelé "algébrique"? Ainsi, les nerds parler de "somme types" et les "types de produits". Par exemple:

data Eg1 = One Int | Two String

Un Eg1 de la valeur est essentiellement soit un entier ou une chaîne de caractères. Ainsi, l'ensemble de tous les possibles Eg1 valeurs est la "somme" de l'ensemble de toutes les valeurs de type entier et de toutes les valeurs de chaîne. Ainsi, les nerds se référer à l' Eg1 comme un "type de somme". Sur l'autre main:

data Eg2 = Pair Int String

Chaque Eg2 de la valeur consiste à la fois un nombre entier et d'une chaîne. Ainsi, l'ensemble de tous les possibles Eg2 valeurs est le produit Cartésien de l'ensemble de tous les nombres entiers et l'ensemble de toutes les chaînes. Les deux ensembles sont "multiplié" ensemble, c'est donc un "type de produit".

Haskell algébrique de types somme types de types de produits. Vous donner un constructeur de multiples domaines pour faire un type de produit, et que vous avez plusieurs constructeurs pour faire une somme (de produits).

Comme un exemple de pourquoi cela pourrait être utile, supposons que vous avez quelque chose que les sorties de données en XML, du JSON, et il faut un enregistrement de la configuration - mais de toute évidence, les paramètres de configuration XML et JSON sont totalement différentes. Donc, vous pourriez faire quelque chose comme ceci:

data Config = XML_Config {...} | JSON_Config {...}

(Avec certains champs convenables là, évidemment.) Vous ne pouvez pas faire des trucs comme ça dans la normale des langages de programmation, qui est pourquoi la plupart des gens ne sont pas habitués à cela.

28voto

Frerich Raabe Points 23711

Commencer avec le cas le plus simple:

data Color = Blue | Green | Red

Ceci définit un "constructeur de type" Color qui ne prend pas d'arguments et il a trois "les constructeurs", Blue, Green et Red. Aucun des constructeurs de données prend aucun argument. Cela signifie qu'il y a trois fonctions (dont aucun ne prenant aucun argument) que vous pouvez utiliser pour créer des valeurs de type Color: Blue, Green et Red.

Données constructeur est utilisé lorsque vous avez besoin pour créer une valeur de quelque sorte. Comme:

myFavoriteColor :: Color
myFavoriteColor = Green

crée une valeur myFavoriteColor à l'aide de l' Green données constructeur et myFavoriteColor sera de type Color puisque c'est le type des valeurs produites par les données constructeur.

Un constructeur de type est utilisé lorsque vous avez besoin pour créer un type quelconque. C'est généralement le cas lors de l'écriture de signatures:

isFavoriteColor :: Color -> Bool

Dans ce cas, vous appelez l' Color constructeur de type (qui ne prend pas d'arguments).

Toujours avec moi?

Maintenant, imaginez que vous pas seulement voulu créer rouge/vert/bleu valeurs, mais vous voulez également spécifier une "intensité". Comme, une valeur entre 0 et 256. Vous pouvez le faire en ajoutant un argument de chacun des constructeurs de données, de sorte que vous vous retrouvez avec:

data Color = Blue Int | Green Int | Red Int

Maintenant, chacun des trois constructeurs de données prend un argument de type Int. Le constructeur de type (Color) encore ne prend aucun argument. Donc, ma couleur préférée étant d'un vert sombre, je pourrais écrire

    myFavoriteColor :: Color
    myFavoriteColor = Green 50

Et encore, il appelle l' Green données constructeur et j'obtiens une valeur de type Color.

Imaginez si vous ne voulez pas dicter la façon dont les gens expriment l'intensité d'une couleur. Certains pourraient vouloir une valeur numérique comme nous venons de faire. D'autres peuvent être bien avec juste un booléen indiquant "lumineux" ou "pas si brillant". La solution c'est de ne pas coder en dur Int dans les données constructeurs, mais plutôt d'utiliser une variable de type:

data Color a = Blue a | Green a | Red a

Maintenant, notre type constructeur prend un argument (un autre type que nous venons d'appel a!) et tous les constructeurs de données va en prendre un argument (une valeur!) de ce type a. Donc vous pourriez avoir

myFavoriteColor :: Color Bool
myFavoriteColor = Green False

ou

myFavoriteColor :: Color Int
myFavoriteColor = Green 50

Remarquez comment nous appelons l' Color type constructeur avec un argument (autre type) pour obtenir le "efficace" type qui sera retourné par les constructeurs de données. Cela touche le concept de genre qui vous voudrez peut-être lire à propos autour d'une tasse de café ou deux.

Maintenant, nous avons compris ce que constructeurs de données et le type de constructeurs, et la façon dont les données constructeurs peuvent prendre d'autres valeurs comme arguments et le type de constructeurs peuvent prendre d'autres types d'arguments. HTH.

6voto

Landei Points 30509

Comme d'autres l'ont souligné, le polymorphisme n'est pas terrible utile ici. Prenons un autre exemple, vous êtes probablement déjà familier avec:

Maybe a = Just a | Nothing

Ce type a deux constructeurs de données. Nothing est un peu ennuyeux, il ne contient pas toutes les données utiles. D'autre part Just contient une valeur de a - quel que soit le type a peut avoir. Nous allons écrire une fonction qui utilise ce type, comme par exemple l'obtention de la tête d' Int la liste, si il y a de tout (j'espère que vous acceptez ce qui est plus utile que de lancer une erreur):

maybeHead :: [Int] -> Maybe Int
maybeHead [] = Nothing
maybeHead (x:_) = Just x

> maybeHead [1,2,3]    -- Just 1
> maybeHead []         -- None

Dans ce cas - a est Int, mais il serait de travailler ainsi que pour tout autre type. En fait, vous pouvez faire de notre fonction de travail pour chaque type de liste (même sans modification de la mise en œuvre):

maybeHead :: [t] -> Maybe t
maybeHead [] = Nothing
maybeHead (x:_) = Just x

D'autre part, vous pouvez écrire des fonctions qui n'accepte qu'un certain type d' Maybe, par exemple

doubleMaybe :: Maybe Int -> Maybe Int
doubleMaybe Just x = Just (2*x)
doubleMaybe Nothing= Nothing

Donc, c'est une longue histoire courte, avec le polymorphisme de vous donner votre propre type de la flexibilité de travailler avec des valeurs de différents autres types.

Dans votre exemple, vous pouvez décider à un certain moment, String n'est pas suffisante pour identifier la société, mais il a besoin d'avoir son propre type Company (qui détient des données supplémentaires, comme le pays, l'adresse, les comptes, etc). Votre première mise en œuvre d' Car auraient besoin de changer d'utiliser Company au lieu de String pour sa première valeur. Votre deuxième application est très bien, vous l'utilisez comme un Car Company String Int et il pourrait fonctionner comme avant (bien sûr fonctions accéder aux données de l'entreprise doivent être changés).

5voto

osager Points 1279

La seconde a la notion de "polymorphisme".

L'a b c peut être de tout type. Par exemple, l'un peut être un [Chaîne], b peut être [Int] et c [Char]

Alors que le premier est de type fixe: la société est une Chaîne de caractères, le modèle est une Chaîne de caractères et l'année est de type Int.

La Voiture, par exemple de ne pas montrer la signification de l'utilisation de polymprphism. Mais imaginez vos données de la liste type. Une liste peut contenir String, Char, Int ... Dans ces situations, vous aurez besoin de la deuxième manière de la définition de vos données.

Quant à la troisième façon, je ne pense pas qu'il doit rentrer dans le type précédent. C'est juste une autre manière de définir les données en Haskell.

C'est à mon humble avis, en tant que débutant moi-même.

Btw: assurez-vous que vous vous entraînez votre cerveau bien et se sentir à l'aise. C'est la clé pour comprendre Monade plus tard.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X