6

Difference between `data` and `newtype` in Haskell and a couple other questions address the general differences between data and newtype. My question is a very specific one. If G is some type, is there any difference between

data T = T !G

and

newtype T = T G?

They appear to have the same strictness properties, and I don't see why the compiler would have any reason to compile them differently, but maybe I'm missing something.

Community
  • 1
  • 1
dfeuer
  • 44,398
  • 3
  • 56
  • 155

2 Answers2

3

The major difference is in how it's gonna get compiled. All data declarations introduce memory overhead, while newtype don't.

Here's an output of a memory footprint measurement library.

import GHC.DataSize

data A = A !Int
newtype B = B Int

main = do
  print =<< (recursiveSize $! A 1)
  print =<< (recursiveSize $! B 1)
  print =<< (recursiveSize $! 1)

output:

32
16
16

Another difference is mentioned by Shachaf in the first comment.

Community
  • 1
  • 1
Nikita Volkov
  • 41,289
  • 10
  • 85
  • 162
  • 10
    There's a difference in semantics: `case undefined of A _ -> ()` is `undefined`; `case undefined of B _ -> ()` is `()`. – shachaf Jan 24 '14 at 08:42
  • @schachaf Wow, that's a case I didn't know about. Thanks. – Nikita Volkov Jan 24 '14 at 08:47
  • @shachaf, I think you hit the nail on the head. I guess pattern matching on a newtype is a no-op, whereas pattern matching on a strict data type is a force. – dfeuer Jan 24 '14 at 09:50
  • what does `!` stand for in `!Int`? – アレックス Dec 22 '14 at 16:45
  • @AlexanderSupertramp It declares that the field should be evaluated strictly instead of lazily (the default). In case of the `newtype` wrapper the wrapped typed can only be strict, so there's no bang required there. – Nikita Volkov Dec 22 '14 at 16:52
2

I'm going to answer a slightly different question: "does newtype add any semantic functionality to Haskell?". I believe the answer is "no".

Suppose we have data Td = Td !G and newtype Tn = Tn G. Then

  • Td and Tn have exactly the same inhabitants, i.e. the inhabitants of G
  • When forced to WHNF both behave in the same way: the g they "contain" is forced
  • They differ in how they interact with case but this is only syntactic. There is a direct correspondance between the two versions.

The following table explains how a newtype Tn can be replaced by a data Td in a case statement. There's also a translation back the other way.

Tn                                    Td

case tn of _ -> ...                   case td of _ -> ...
case tn of Tn _ -> ...

case tn of Tn x -> ... x ...          let x1 = case tn of Td x -> x in ... x1 ...

case tn of x -> ... x ...             case td of x -> ... x ...

case tn of Tn x -> x `seq` ...        case td of Td _ -> ...         

case tn of Tn x -> x `seq` ... x ...  case td of Td x -> ... x ...     

So semantically speaking I believe Haskell could have avoided adding newtype. Syntactically speaking newtype maybe makes case statements a less awkward, that's all.

Tom Ellis
  • 8,007
  • 21
  • 45