Newtype é uma declaração de tipo de dados especializada. De modo que ele contenha apenas um construtor e campo.
newtype Foo a = Bar a newtype Id = MkId Word 

Perguntas típicas para iniciantes
Qual é a diferença dos dados do tipo de dados? data Foo a = Bar a data Id = MkId Word 
A principal especificidade do 
newtype é que ele consiste nas mesmas partes que seu único campo. Mais precisamente, difere do original no nível de tipo, mas tem a mesma representação na memória e é calculada estritamente (não preguiçosamente).
Em resumo - o 
newtype é mais eficaz devido à sua apresentação.
Sim, isso não significa nada para mim ... vou usar dadosNão, bem, no final, você sempre pode ativar a extensão 
-funpack-strict-fields :) para 
campos estritos (não preguiçosos) ou especificar diretamente
 data Id = MkId !Word 
Ainda assim, o poder do 
novo tipo não se limita à eficiência computacional. Eles são muito mais fortes! 
3 funções de tipo novo

Ocultando a implementação
 module Data.Id (Id()) where newtype Id = MkId Word 
newtype difere do original, apenas internamente o 
Word .
Mas 
ocultamos o construtor 
MkId fora do módulo.
Implementação Implementação
 {-# LANGUAGE GeneralizedNewtypeDeriving #-} newtype Id = MkId Word deriving (Num, Eq) 
Embora isso não esteja no padrão Haskell2010, expandindo a saída de newTypes genéricos, você pode inferir automaticamente o comportamento de 
newtype da mesma forma que o comportamento do campo interno. No nosso caso, o comportamento de 
Eq Id e 
Num Id é o mesmo que 
Eq Word e 
Num Word .
Muito mais pode ser alcançado através da expansão da criação refinada ( 
DerivingVia ), mas mais sobre isso mais tarde.
Implementação de escolha
Apesar de seu próprio construtor, em alguns casos você pode usar sua representação interna.
Desafio
Há uma lista de números inteiros. Encontre o valor máximo e total em apenas um passe na lista.
E não use 
pacotes foldl e 
folds .
Resposta típica
Claro, 
dobre ! :)
 foldr :: Foldable t => (a -> b -> b) -> b -> ta -> b  
E, a função final é descrita assim:
 aggregate :: [Integer] -> (Maybe Integer, Integer) aggregate = foldr (\el (m, s) -> (Just el `max` m, el + s)) (Nothing, 0)  
Se você observar atentamente, poderá ver operações semelhantes nos dois lados: 
Apenas el `max` me el + s . Nos dois casos, mapeamento e operação binária. E os elementos vazios são 
Nothing e 
0 .
Sim, estes são monoides!
Monóide e Semigrupo em mais detalhesUm semigrupo é uma propriedade de uma operação binária associativa
 x ⋄ (y ⋄ z) == (x ⋄ y) ⋄ z 
Um monóide é uma propriedade de uma operação associativa (ou seja, um semigrupo)
 x ⋄ (y ⋄ z) == (x ⋄ y) ⋄ z 
que possui um elemento vazio que não altera nenhum elemento à direita ou à esquerda
 x ⋄ empty == x == empty ⋄ x 
 Ambos 
max e 
(+) são associativos, ambos possuem elementos vazios - 
Nothing e 
0 .
E a combinação de mapeamento de monoides junto com a convolução é 
dobrável !
Dobrável mais detalhadoLembre-se da definição de dobrar:
 class Foldable t where foldMap :: (Monoid m) => (a -> m) -> ta -> m ... 
 Vamos aplicar o comportamento de dobrar para 
max e 
(+) . Não podemos organizar mais que uma implementação do 
Word monóide. Está na hora de tirar proveito da implementação da opção 
newtype !
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}  
É necessário fazer uma observação.
O fato é que, para ser um monóide para o tipo de dados 
Max , precisamos de um elemento mínimo, ou seja, para que exista um elemento vazio. Portanto, apenas um 
Max a limitado pode ser um monóide.
Teoricamente correto, elemento máximo monóide newtype Max a = Max a instance Ord a => Semigroup (Max a) instance Bounded a => Monoid (Max a) 
 Então, de alguma forma, teremos que converter nosso tipo de dados para que um elemento vazio apareça e possamos usar a coagulação.
 
O elemento conjugado 
Maybe transforma um semigrupo em monóide!
Liberalização de restrições nas versões recentes do GHCDe volta ao GHC 8.2, era necessário um monóide na restrição de tipo
 instance Monoid a => Monoid (Maybe a) 
o que significa que precisávamos de outro tipo:
 
E já é muito mais simples no GHC 8.4, onde apenas um semigrupo com restrição de tipo é necessário, e mesmo não há necessidade de criar um tipo de opção.
 instance Semigroup a => Monoid (Maybe a) 
 Resposta dobrável
Bem, agora atualize o código usando recolhibilidade e setas.
Lembramos que (.) É apenas uma composição funcional:
  (.) :: (b -> c) -> (a -> b) -> a -> c f . g = \x -> f (gx) 
E lembre-se de que o 
fmap é um functor:
 fmap :: Functor f => (a -> b) -> fa -> fb 
e sua implementação para 
Maybe é descrita um pouco mais.
Seta mais detalhadaAs setas são propriedades de algumas funções que permitem trabalhar com elas em um diagrama de blocos.
Mais detalhes podem ser encontrados aqui: 
Setas: uma interface geral para computaçãoNo nosso caso, usamos a função Arrows
Isso é
 instance Arrow (->) 
Vamos usar as funções:
 (***) :: Arrow a => abc -> ab' c' -> a (b, b') (c, c') (&&&) :: Arrow a => abc -> abc' -> ab (c, c') 
Para o nosso caso
 abc == (->) bc == b -> c 
E, consequentemente, a assinatura de nossas funções é reduzida para:
 (***) :: (b -> c) -> (b' -> c') -> ((b, b') -> (c, c')) (&&&) :: (b -> c) -> (b -> c') -> (b -> (c, c')) 
Ou, em palavras bastante simples, a função 
(***) combina duas funções com um argumento (e um tipo de saída) em uma função com o trabalho de um par de argumentos na entrada e na saída, respectivamente, um par de tipos de saída.
A função 
(&&&) é uma versão simplificada 
(***) , em que o tipo dos argumentos de entrada das duas funções é o mesmo, e na entrada não temos um par de argumentos, mas um argumento.
 Total, a função unificadora adquiriu a forma:
 import Data.Semigroup import Data.Monoid import Control.Arrow aggregate :: [Integer] -> (Maybe Integer, Integer) aggregate = (fmap getMax *** getSum) . (foldMap (Just . Max &&& Sum))  
Acabou muito brevemente!
Mas, ainda é cansativo quebrar e inverter dados de tipos aninhados!
Você pode reduzi-lo ainda mais, e uma conversão forçada sem recursos nos ajudará!
Conversão forçada segura e sem recursos e funções de tipo
Existe uma função do pacote 
Unsafe.Coerce - 
unsafeCoerce import Unsafe.Coerce(unsafeCoerce) unsafeCoerce :: a -> b 
A função insegura à força converte o tipo: de 
a para 
b .
De fato, a função é mágica, diz ao compilador para considerar os dados do tipo 
a como tipo 
b , sem levar em consideração as conseqüências desta etapa.
Pode ser usado para converter tipos aninhados, mas você precisa ter muito cuidado.
Em 2014, houve uma revolução com o 
newtype , a saber, uma conversão forçada, segura e sem recursos!
 import Data.Coerce(coerce) coerce :: Coercible ab => a -> b 
Esse recurso abriu uma nova era no trabalho com 
newtype .
O Coercible Forced Converter funciona com tipos que têm a mesma estrutura na memória. Parece uma classe de tipo, mas, na verdade, o GHC converte tipos em tempo de compilação e não é possível definir instâncias por conta própria.
A função 
Data.Coerce.coerce permite converter tipos sem recursos, mas para isso precisamos ter acesso aos construtores de tipos.
Agora simplifique nossa função:
 import Data.Semigroup import Data.Monoid import Control.Arrow import Data.Coerce aggregate :: [Integer] -> (Maybe Integer, Integer) aggregate = coerce . (foldMap (Just . Max &&& Sum))  
Evitamos a rotina de puxar tipos aninhados; fizemos isso sem desperdiçar recursos com apenas uma função.
Funções dos tipos de dados aninhados
Com a função 
coagir, podemos forçar a conversão de qualquer tipo aninhado.
Mas é necessário usar esse recurso tão amplamente?
 
Semanticamente, é um absurdo converter para 
Sorted a de 
Sorted (Abaixo a) .
No entanto, você pode tentar:
 ghci> let h = fromList2Sorted [1,2,3] :: Sorted Int ghci> let hDown = fromList2Sorted $ fmap Down [1,2,3] :: Sorted (Down Int) ghci> minView h Just (Down 1) ghci> minView (coerce h :: Sorted (Down Int)) Just (Down 1) ghci> minView hDown Just (Down 3) 
Tudo ficaria bem, mas a resposta correta é 
Just (Down 3) .
Ou seja, para eliminar o comportamento incorreto, as funções de tipo foram introduzidas.
 {-# LANGUAGE RoleAnnotations #-} type role Sorted nominal 
Vamos tentar agora:
 ghci> minView (coerce h :: Sorted (Down Int)) error: Couldn't match type 'Int' with 'Down Int' arising from a use of 'coerce' 
Significativamente melhor!
No total, existem 3 papéis ( 
tipo papel ):
- representacional - equivalente se a mesma representação
- nominal - deve ter exatamente o mesmo tipo
- fantasma - independente do conteúdo real. Equivalente a qualquer coisa
Na maioria dos casos, o compilador é inteligente o suficiente para revelar o papel do tipo, mas pode ser ajudado.
Comportamento de derivação refinada
Graças à expansão da linguagem 
DerivingVia , a função de distribuição do 
newtype melhorou.
A partir do GHC 8.6, lançado recentemente, essa nova extensão apareceu.
 {-# LANGUAGE DerivingVia #-} newtype Id = MkId Word deriving (Semigroup, Monoid) via Max Word 
Como você pode ver, o comportamento do tipo é inferido automaticamente devido ao refinamento de como produzir.
O DerivingVia pode ser aplicado a qualquer tipo que ofereça suporte ao 
Coercible e o 
mais importante - completamente sem consumo de recursos!
Ainda mais, o 
DerivingVia pode ser aplicado não apenas ao 
newtype , mas também a qualquer tipo isomórfico, se eles suportarem 
genéricos genéricos e conversão forçada 
coercível .
Conclusões
Tipos 
newtype é uma força poderosa que simplifica e aprimora muito o código, elimina a rotina e reduz o consumo de recursos.
Tradução original : 
O grande poder dos novos tipos (Hiromi Ishii)PS Acho que, depois deste artigo, publicado há mais de um ano [não pelo meu] artigo, a magia do newtype em Haskell sobre novos tipos se tornará um pouco mais clara!