tl;dr ()
ne pas ajouter une valeur "null pour chaque type, l'enfer non; ()
est un "terne" valeur dans un type de son propre: ()
.
Permettez-moi de prendre du recul à partir de la question un instant et l'adresse d'une source de confusion. Un truc important à absorber lors de l'apprentissage de Haskell est la distinction entre l' expression de la langue et de son type de langue. Vous êtes probablement au courant que les deux sont séparés. Mais que permet le même symbole pour être utilisés dans les deux, et c'est ce qui se passe ici. Il y a de simples indices textuels pour vous dire la langue dans laquelle vous êtes en train de regarder. Vous n'avez pas besoin d'analyser l'ensemble de la langue pour la détection de ces signaux.
Le haut niveau de Haskell module de vie, par défaut, dans le langage d'expression. Vous définissez les fonctions à écrire les équations entre les expressions. Mais quand vous voyez foo :: bar dans le langage d'expression, cela signifie que foo est l'expression et la barre est son type. Donc, quand vous lisez () :: ()
, vous voyez une déclaration qui concerne l' ()
dans l'expression de la langue avec l' ()
dans le type de langue. Les deux ()
symboles signifient des choses différentes, parce qu'ils ne sont pas dans la même langue. Cette répétition souvent source de confusion pour les débutants, jusqu'à ce que l'expression/type de langue séparation intalls lui-même dans son subconscient, à quel point il devient utilement mnémonique.
Le mot-clé data
introduit un nouveau type de données de la déclaration, impliquant un mélange soigneux de l'expression et le type de langues, comme il est dit d'abord ce que le nouveau type est, et, deuxièmement, quelles sont ses valeurs.
les données TyCon tyvar ... tyvar = ValCon1 type ... type | ... | ValConn type type ...
Dans cette déclaration, constructeur de type TyCon est ajoutée à la type de la langue et de la ValCon valeur les constructeurs sont en train d'être ajouté à l'expression de la langue (et son modèle de sous-langue). En data
déclaration, les choses qui s'opposent à l'argument des lieux de la ValCon s vous dire le type donné les arguments lorsque que ValCon est utilisé dans les expressions. Par exemple,
data Tree a = Leaf | Node (Tree a) a (Tree a)
déclare un constructeur de type Tree
pour arbre binaire types de stockage des éléments aux nœuds, dont les valeurs sont données en valeur les constructeurs Leaf
et Node
. J'aime la couleur type de constructeurs (Arbre) de bleu et de la valeur de constructeurs (Leaf, Node) en rouge. Il devrait y avoir pas de bleu dans les expressions et (sauf si vous utilisez des fonctionnalités avancées) pas de rouge dans les types de. Le type Bool
pourrait être déclarée,
data Bool = True | False
ajouter du bleu Bool
le type de langue, et rouge True
et False
de l'expression de la langue. Malheureusement, mon markdown-fu est insuffisante pour la tâche d'ajouter des couleurs à ce poste, de sorte que vous aurez à apprendre à ajouter les couleurs dans votre tête.
L ' "unité" type utilise ()
comme un symbole particulier, mais il fonctionne que s'il est déclaré
data () = () -- the left () is blue; the right () is red
ce qui signifie qu'une théorie bleu ()
est un constructeur de type dans le genre de la langue, mais qu'une théorie, rouge, ()
est un constructeur de valeurs dans l'expression de la langue, et en effet () :: ()
. [Il n'est pas le seul exemple d'un jeu de mots. Les types de la plus grande des tuples suivent le même modèle: paire de syntaxe est comme si
data (a, b) = (a, b)
l'ajout de (,) à la fois le type et expression, langues. Mais je m'éloigne du sujet.]
Donc, le type ()
, souvent prononcé "Unité", est un type contenant une valeur qui mérite d'être parlant d': cette valeur est écrite ()
, mais dans le langage d'expression, et est parfois prononcé "vide". Un type avec une seule valeur n'est pas très intéressant. Une valeur de type ()
contribue à zéro des bits d'information: vous savez déjà ce qui doit être. Ainsi, alors qu'il n'est rien de spécial à propos du type ()
à indiquer des effets secondaires, il apparaît souvent que la valeur de la composante dans un type monadique. Monadique opérations ont tendance à avoir des types qui ressemblent à des
val-de-type-1 -> ... -> val-de-type-n -> effets-la monade val-hors-type
où le type de retour est un type d'application: la fonction vous indique quels sont les effets possibles et l'argument que vous dit ce genre de valeur est produite par l'opération. Par exemple,
put :: s -> State s ()
ce qui est lu (parce que d'application associés à gauche ["comme nous l'avons tous fait dans les années soixante", Roger Hindley]) comme
put :: s -> (State s) ()
a une entrée de valeur type s
, l'effet-monade State s
, et la valeur de sortie de type ()
. Quand vous voyez ()
de la valeur que le type de sortie, qui signifie simplement "cette opération est utilisée uniquement pour son effet; la valeur est inintéressant". De la même façon
putStr :: String -> IO ()
fournit une chaîne d' stdout
mais ne retourne rien d'excitant.
L' ()
type est également utile comme un type d'élément pour les conteneurs comme des structures, où il indique que les données se compose simplement d'une forme, avec pas intéressant de charge utile. Par exemple, si Tree
est déclarée comme ci-dessus, puis Tree ()
est le type d'arbre binaire de formes, de stockage de rien d'intérêts au niveau des nœuds. De même, [()]
est le type de listes d'terne éléments: et si il n'y a rien d'intéressant dans une liste d'éléments, alors la seule information dont il contribue, c'est sa durée.
Pour résumer, ()
est un type. Sa seule valeur, ()
, arrive à avoir le même nom, mais c'est ok parce que le type et l'expression sont des langues distinctes. Il est utile d'avoir un type qui représente "pas d'information" parce que, dans le contexte (par exemple, d'une monade ou un conteneur), il vous indique que seul le contexte est intéressant.