Différence entre `data` et` newtype` dans Haskell

191

Quelle est la différence lorsque j'écris ceci?

data Book = Book Int Int

contre

newtype Book = Book (Int, Int) -- "Book Int Int" is syntactically invalid
ewggwegw
la source
Vous devriez faire quelques recherches, cette question a déjà été répondue. stackoverflow.com/questions/2649305/…
tehman
Également en relation: utilisations pour newtype: stackoverflow.com/questions/991467/…
Don Stewart
25
Notez que ce newtype Book = Book Int Intn'est pas valide. Vous pouvez cependant avoir newtype Book = Book (Int, Int)comme indiqué par les dons ci-dessous.
Edward KMETT

Réponses:

241

Excellente question!

Il existe plusieurs différences clés.

Représentation

  • A newtypegarantit que vos données auront exactement la même représentation au moment de l'exécution, que le type que vous encapsulez.
  • While datadé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 newtypeest garantie d'être effacée au moment de la compilation.

Exemples:

  • data Book = Book Int Int

Les données

  • newtype Book = Book (Int, Int)

nouveau genre

Notez comment il a exactement la même représentation que a (Int,Int), puisque le Bookconstructeur est effacé.

  • data Book = Book (Int, Int)

tuple de données

Possède un Bookconstructeur supplémentaire non présent dans le newtype.

  • data Book = Book {-# UNPACK #-}!Int {-# UNPACK #-}!Int

entrez la description de l'image ici

Pas de pointeurs! Les deux Intchamps sont des champs de la taille d'un mot sans boîte dans le Bookconstructeur.

Types de données algébriques

En raison de ce besoin d'effacer le constructeur, a newtypene 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 dataet newtype. En particulier, dataintroduit 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 Bookau (,)constructeur nous permet de mettre une valeur inférieure à.

En conséquence, newtypeet dataont 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 Tconstructeur et un Int#composant. Vous obtenez juste un nu Intavec newtype.


Références :

Don Stewart
la source
2
Je ne pense toujours pas que je manquerais quelque chose s'il n'y avait pas de "newtype" dans Haskell. Les différences subtiles ajoutent de la complexité au langage qui ne me semble pas utile ...
martingw
14
La différence est très utile pour des raisons de performances. Étant donné que les constructeurs de newtype sont effacés au moment de la compilation, ils n'imposent pas la pénalité des performances d'exécution d'un constructeur de données. Mais ils vous donnent toujours tous les avantages d'un type complètement distinct et des abstractions que vous souhaitez y associer. Par exemple, il existe deux manières différentes pour le type de données de liste de former une monade. L'un est intégré au langage, mais si vous vouliez utiliser l'autre, un nouveau type serait la voie à suivre.
mightybyte
Excellente explication! Ce que je ne comprends pas, c'est que si newtypeest effacé après la compilation et que l'exécution utilise la même représentation pour les anciens et les nouveaux types, comment pouvons-nous encore être en mesure de définir des instances pour l'ancien et le nouveau type? Comment le runtime peut-il comprendre quelle instance utiliser?
damluar le
3
@damluar Tous les types sont effacés au moment de l'exécution, ils sont tous entièrement résolus au moment de la compilation, et lors de la compilation, ils ne sont newtypeévidemment pas encore effacés.
point
3
@damlaur J'ai eu une fois la même question que vous. Lorsque les gens disent que les types sont effacés, ils omettent de mentionner qu'une chose n'est PAS effacée, à savoir un mot mémoire utilisé pour les recherches dans le dictionnaire pour décider de la méthode d'instance à utiliser pour une donnée donnée. Les gens soutiennent que ce mot n'est pas un «type», ce qui, je pense, dépend de votre point de vue, mais voilà.
Gabriel L.