Ótima pergunta!
Existem várias diferenças importantes.
Representação
- A
newtypegarante que seus dados tenham exatamente a mesma representação em tempo de execução, como o tipo que você agrupa.
- Enquanto
datadeclara uma nova estrutura de dados em tempo de execução.
Portanto, o ponto principal aqui é que a construção para o newtypeé garantida para ser apagada no tempo de compilação.
Exemplos:

newtype Book = Book (Int, Int)

Observe como ele tem exatamente a mesma representação que um (Int,Int), já que o Bookconstrutor é apagado.
data Book = Book (Int, Int)

Tem um Bookconstrutor adicional não presente no arquivo newtype.
data Book = Book {-# UNPACK #-}!Int {-# UNPACK #-}!Int

Sem ponteiros! Os dois Intcampos são campos com tamanho de palavra sem caixa no Bookconstrutor.
Tipos de dados algébricos
Devido a essa necessidade de apagar o construtor, newtypesó funciona ao agrupar um tipo de dados com um único construtor . Não há noção de novos tipos "algébricos". Ou seja, você não pode escrever um novo tipo equivalente a, digamos,
data Maybe a = Nothing
| Just a
já que possui mais de um construtor. Nem você pode escrever
newtype Book = Book Int Int
Rigor
O fato de o construtor ser apagado leva a algumas diferenças muito sutis no rigor entre datae newtype. Em particular, dataintroduz um tipo que é "elevado", significando, essencialmente, que ele tem uma maneira adicional de avaliar o valor mais baixo. Como não há construtor adicional em tempo de execução newtype, essa propriedade não é válida.
Esse ponteiro extra no Bookpara (,)construtor nos permite colocar um valor inferior em.
Como resultado, newtypee datapossui propriedades de rigidez ligeiramente diferentes, conforme explicado no artigo da wiki Haskell .
Desembalagem
Não faz sentido desmarcar os componentes de a newtype, pois não há construtor. Embora seja perfeitamente razoável escrever:
data T = T {-# UNPACK #-}!Int
produzindo um objeto de tempo de execução com um Tconstrutor e um Int#componente. Você só obter um nu Intcom newtype.
Referências :