Excellente question!
Il existe plusieurs différences clés.
Représentation
- A
newtype
garantit que vos données auront exactement la même représentation au moment de l'exécution, que le type que vous encapsulez.
- While
data
déclare une toute nouvelle structure de données au moment de l'exécution.
Donc, le point clé ici est que la construction pour le newtype
est garantie d'être effacée au moment de la compilation.
Exemples:
newtype Book = Book (Int, Int)
Notez comment il a exactement la même représentation que a (Int,Int)
, puisque le Book
constructeur est effacé.
data Book = Book (Int, Int)
Possède un Book
constructeur supplémentaire non présent dans le newtype
.
data Book = Book {-# UNPACK #-}!Int {-# UNPACK #-}!Int
Pas de pointeurs! Les deux Int
champs sont des champs de la taille d'un mot sans boîte dans le Book
constructeur.
Types de données algébriques
En raison de ce besoin d'effacer le constructeur, a newtype
ne fonctionne que lors de l'encapsulation d'un type de données avec un seul constructeur . Il n'y a pas de notion de newtypes "algébriques". Autrement dit, vous ne pouvez pas écrire un équivalent newtype de, disons,
data Maybe a = Nothing
| Just a
car il a plus d'un constructeur. Tu ne peux pas non plus écrire
newtype Book = Book Int Int
Rigueur
Le fait que le constructeur soit effacé conduit à des différences très subtiles de rigueur entre data
et newtype
. En particulier, data
introduit un type qui est "soulevé", ce qui signifie, essentiellement, qu'il a un moyen supplémentaire d'évaluer à une valeur inférieure. Comme il n'y a pas de constructeur supplémentaire au moment de l'exécution avec newtype
, cette propriété ne tient pas.
Ce pointeur supplémentaire dans le Book
au (,)
constructeur nous permet de mettre une valeur inférieure à.
En conséquence, newtype
et data
ont des propriétés de rigueur légèrement différentes, comme expliqué dans l'article du wiki Haskell .
Déballage
Cela n'a pas de sens de déballer les composants de a newtype
, car il n'y a pas de constructeur. S'il est parfaitement raisonnable d'écrire:
data T = T {-# UNPACK #-}!Int
produisant un objet d'exécution avec un T
constructeur et un Int#
composant. Vous obtenez juste un nu Int
avec newtype
.
Références :