2011-08-07 15 views
9

Esta es una pregunta tonta que me ha estado molestando un poco. Por qué no puedo escribir un newtype con múltiples parámetros,new-parameter newtype falso con una tupla?

newtype A = A Int Int 

mientras que la versión tupla está bien?

newtype A = A (Int, Int) 

la primera es mucho mejor en aspectos como la coincidencia de patrones.

+2

has leído [this] (http://www.haskell.org/haskellwiki/Newtype)? –

+0

@ n.m., Gracias por el enlace, pero ¿podría dar más detalles sobre las preocupaciones prácticas? ¿Tiene que ver con emparejar '(A _ _)'? No entiendo por qué la equivalencia de la tupla llevaría a un comportamiento poco intuitivo. – gatoatigrado

Respuesta

10

newtype A = A Int crea un tipo isomorfo a Int. Es decir, se comporta exactamente como Int w.r.t. p.ej. bottom, pero con un nombre diferente.

Esto está en contraste con data A = A Int que crea un tipo levantado que se comporta de forma diferente a Int. Hay otro valor agregado que no está en Int: A undefined (que es distinto de undefined::A).

Ahora, newtype A = A (Int, Int) crea un tipo isomorfo a (Int, Int). Que es, por cierto, exactamente lo que data A = A Int Int hace.

Entonces, si admitimos que newtype A = A Int Int es equivalente a newtype A = A (Int, Int), ¿qué tenemos? newtype A = A Int Int es equivalente a newtype A = A (Int, Int) que es equivalente a data A = A Int Int.

newtype A = A Int Int es equivalente a data A = A Int Int (así newtype es redundante en este caso), pero

newtype A = A Int es no equivalente a data A = A Int (que es el punto de tener newtype en el primer lugar).

Así que debemos concluir que newtype A = A Int Int es equivalente a newtype A = A (Int, Int) crea redundancia e incoherencia, y es mejor que no lo permitamos.

Probablemente no hay manera de dar newtype A = A Int Int algún otro significado que está libre de estas inconsistencias (o de lo contrario podría ser encontrado y utilizado supongo;)

7

Debido a que un newtype, en términos generales, funciona como type en tiempo de ejecución y como data en tiempo de compilación. Cada definición de data agrega una capa adicional de direccionamiento indirecto, que, en circunstancias normales, significa otro lugar distinto donde algo se puede dejar como un thunk, alrededor de los valores que contiene, mientras que newtype no lo hace. El "constructor" en un newtype es básicamente una ilusión.

Cualquier cosa que combina múltiples valores en una sola, o que le da una elección entre varios casos, necesariamente se introduce una capa de indirección para expresar que, por lo que la interpretación lógica de newtype A = A Int Int sería dos desconectados Int valores con nada "manteniéndolas juntas" . La diferencia en el caso de newtype A = A (Int, Int) es que la tupla misma agrega la capa adicional de direccionamiento indirecto.

Contraste esto con data A = A Int Int contra data A = A (Int, Int).El primero agrega una capa (el constructor A) alrededor de los dos Int s, mientras que el segundo agrega la misma capa alrededor de la tupla, que a su vez agrega una capa alrededor de los Int s.

Cada capa de indirección también generalmente agrega un lugar donde algo puede ser ⊥, así que considere los posibles casos para cada forma, ¿dónde? representa un valor no inferior:

  • Para newtype A = A (Int, Int): , (⊥, ?), (?, ⊥), (?, ?)

  • Para data A = A Int Int: , A ⊥ ?, A ? ⊥, A ? ?

  • Para data A = A (Int, Int): , A ⊥, A (⊥, ?), A (?, ⊥), A (?, ?)

Como se puede ver de lo anterior, los dos primeros son equivalentes.


En una nota final, he aquí una divertida demostración de cuán newtype difiere de data. Tenga en cuenta estas definiciones:

data D = D D deriving Show 
newtype N = N N deriving Show 

¿Qué valores posibles, incluidos todos los posibles dos, tiene cada uno de estos? ¿Y cuál crees que serán los dos valores a continuación?

d = let (D x) = undefined in show x 
n = let (N x) = undefined in show x 

¡Cárguelos en GHC y descubra!