Simple version for folks obsessed with bullet lists (failed to find one, so have to write it by myself):
data - creates new algebraic type with value constructors
- Can have several value constructors
- Value constructors are lazy
- Values can have several fields
- Affects both compilation and runtime, have runtime overhead
- Created type is a distinct new type
- Can have its own type class instances
- When pattern matching against value constructors, WILL be evaluated at least to weak head normal form (WHNF) *
- Used to create new data type (example: Address { zip :: String, street :: String } )
newtype - creates new “decorating” type with value constructor
- Can have only one value constructor
- Value constructor is strict
- Value can have only one field
- Affects only compilation, no runtime overhead
- Created type is a distinct new type
- Can have its own type class instances
- When pattern matching against value constructor, CAN be not evaluated at all *
- Used to create higher level concept based on existing type with distinct set of supported operations or that is not interchangeable with original type (example: Meter, Cm, Feet is Double)
type - creates an alternative name (synonym) for a type (like typedef in C)
- No value constructors
- No fields
- Affects only compilation, no runtime overhead
- No new type is created (only a new name for existing type)
- Can NOT have its own type class instances
- When pattern matching against data constructor, behaves the same as original type
- Used to create higher level concept based on existing type with the same set of supported operations (example: String is [Char])
[*] On pattern matching laziness:
data DataBox a = DataBox Int
newtype NewtypeBox a = NewtypeBox Int
dataMatcher :: DataBox -> String
dataMatcher (DataBox _) = "data"
newtypeMatcher :: NewtypeBox -> String
newtypeMatcher (NewtypeBox _) = "newtype"
ghci> dataMatcher undefined
"*** Exception: Prelude.undefined
ghci> newtypeMatcher undefined
“newtype"