This site uses cookies to deliver our services and to show you relevant ads and job listings.
By using our site, you acknowledge that you have read and understand our Cookie Policy, Privacy Policy, and our Terms of Service.
Your use of Stack Overflow’s Products and Services, including the Stack Overflow Network, is subject to these policies and terms.

Join us in building a kind, collaborative learning community via our updated
Code of Conduct.

1 Answer
1

A newtype guarantees that your data will have exactly the same representation at runtime, as the type that you wrap.

While data declares a brand new data structure at runtime.

So the key point here is that the construct for the newtype is guaranteed to be erased at compile time.

Examples:

data Book = Book Int Int

newtype Book = Book (Int, Int)

Note how it has exactly the same representation as a (Int,Int), since the Book constructor is erased.

data Book = Book (Int, Int)

Has an additional Book constructor not present in the newtype.

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

No pointers! The two Int fields are unboxed word-sized fields in the Book constructor.

Algebraic data types

Because of this need to erase the constructor, a newtype only works when wrapping a data type with a single constructor. There's no notion of "algebraic" newtypes. That is, you can't write a newtype equivalent of, say,

data Maybe a = Nothing
| Just a

since it has more than one constructor. Nor can you write

newtype Book = Book Int Int

Strictness

The fact that the constructor is erased leads to some very subtle differences in strictness between data and newtype. In particular, data introduces a type that is "lifted", meaning, essentially, that it has an additional way to evaluate to a bottom value. Since there's no additional constructor at runtime with newtype, this property doesn't hold.

That extra pointer in the Book to (,) constructor allows us to put a bottom value in.

I still don't think I'd miss something if there was no "newtype" in Haskell. The subtle differences add complexity to the language that don't seem worthwile to me...
– martingwMay 6 '11 at 11:47

11

The difference is very useful for performance reasons. Since newtype constructors are erased at compile time, they don't impose the runtime performance penalty that a data constructor does. But they still give you all the benefits of a completely distinct type and whatever abstractions you want to associate with it. For instance, there are two different ways the list data type can form a monad. One is built into the language, but if you wanted to use the other one, a newtype would be the way to go.
– mightybyteMay 6 '11 at 12:59

Great explanation! What I don't understand is if newtype is erased after compilation and runtime uses the same representation for old and new types, how can we still be able to define instances for both old and new type? How can runtime understand which instance to use?
– damluarFeb 9 '16 at 11:07

3

@damluar All types are erased at runtime, they are all fully resolved at compile time, and during compilation newtype is obviously not yet erased.
– semicolonApr 21 '16 at 18:32