2012-09-09 28 views
6

Comencé esta nueva pregunta porque se convirtió en un seguimiento de mi pregunta anterior.Uso de tipos de datos en Haskell

Si tengo dos tipos de datos que se componen de constructores similares:

data A = X | Y | Z 
data B = X | Y 

¿No hay manera alguna manera puedo representar esto como:

data A = C | Z 
data B = C 

data C = X | Y 

si se puede ver lo que estoy haciendo - Estoy tratando de agrupar el X | Y en un tipo de datos, que luego puede ser utilizado por muchos otros tipos de datos. Parece que no puedo obtener el compilador para permitir esto, o si lo hace, no puedo hacer coincidir patrones con X o Y, solo con C?

Aparece el mensaje de error que C ha sido declarado varias veces.

Pensé que podría usar tipos, pero no permiten el uso de varias tipificaciones.

EDITAR

Incluso si declaro el camino más largo (como abajo), todavía no se compilará y dice que X e Y tienen múltiples declaraciones:

data A = X | Y | Z 
data B = X | Y 
+0

Lo que están pidiendo sería un subtipo de 'A '. No se declararía con la palabra clave 'data', que crea un nuevo tipo, disjunta de los tipos existentes anteriores. No creo que Haskell tenga esa característica, pero no estoy al tanto de todas las extensiones de Haskell. – Gilles

+0

@Gilles: No, Haskell no tiene ningún polimorfismo de subtipo. Solo tiene polimorfismo paramétrico y polimorfismo ad-hoc a través de clases de tipos. Lo más cercano que puedes obtener es un tipo existencial, pero eso es casi, pero no del todo, una cosa completamente diferente. –

+0

Me gustaría poner esto como una respuesta, pero porque no es del todo ... Es posible que pueda acercarse a lo que desea al declarar una clase de tipo y luego las operaciones que necesita para esas "cosas comunes". Esta es una forma bastante común de cambiar las cosas para resolver (una versión de) este problema. –

Respuesta

13

No sólo no se puede hacer esto, tampoco puede hacer su primera opción, es decir, no puede tener dos tipos en el mismo módulo que ambos tienen constructores llamados X y Y.

Si pudiera hacer esto, ¿cuál debería ser el tipo de X ser - C, A o B? La respuesta más obvia sería C, pero no podrá usarla en un contexto en el que se requiera A o B (tenga en cuenta que Haskell no tiene subtipos), por lo que se anularía el propósito de toda la construcción.

Lo mejor que puede hacer es envolver C en un constructor de A y B, es decir:

data A = AC C | Z 
data B = BC C 
data C = X | Y 

Posteriormente, se podría envolver un C, ya sea con la AC o la BC constructor para crear un valor de tipo A o B respectivamente.

+0

Acaba de editar para decir que mi primera opción no funciona. Cuando dices "envolver", literalmente quieres decir "incluir alguna otra sintaxis delante de C, para distinguirla de la C original". – Lethi

+2

@Dan Sí. En mi ejemplo, escribiría 'AC X' para crear un valor de tipo' A' y 'BC X' para crear el valor de tipo' B'. 'X' en sí mismo no puede tener dos tipos diferentes. – sepp2k

4

La razón por la que no se puede hacer esto

data A = X | Y | Z 
data B = X | Y 

es el siguiente. Digamos que escribir un código más adelante:

foo n = (n,X) 

que construye un par que consiste en n en la primera ranura y X en la segunda ranura. ¿Qué tipo debe inferir el compilador? Un tipo válido sería

foo :: a -> A -> (a,A) 

desde X es un constructor de tipo A, pero igualmente válido es

foo :: a -> B -> (a,B) 

desde X es un constructor de tipo B.Si tiene dos constructores con el mismo nombre, no puede inferir un tipo único para las funciones que los usan. Por lo tanto, no se permite dar dos constructores en el mismo módulo con el mismo nombre.

1

No se puede hacer esto:

data A = C | Z 
data B = C 

data C = X | Y 

(Dicho sea de paso, si es Bidéntica aC, entonces ¿por qué tiene B en absoluto?)

Pero lo que puede hacer es algo como esto:

data A = A_Other C | Z 
data B = B_Other C 

data C = X | Y 

Luego Puede coincidir con el patrón de esta:

foo :: A -> String 
foo (A_Other X) = "X" 
foo (A_Other Y) = "Y" 
foo (  Z) = "Z" 

bar :: B -> String 
bar (B_Other X) = "X" 
bar (B_Other Y) = "Y" 

foobar :: C -> String 
foobar X = "X" 
foobar Y = "Y" 

Si eso tiene sentido ...

0

No se puede hacer lo que quiera, porque está declarando varios constructores de datos. En

data A = X | Y | Z 

En realidad se está introduciendo el tipo A que tiene 3 constructores (valores) X, Y, y Z. Esta es la razón por la cual su primer código no compilará, ¡verá el mismo nombre listado como constructores para dos tipos diferentes! Si usted puede hacer esto habría que preguntarse es

X :: A 

o

X :: B 

que, en un contexto no orientado a objetos es miedo! Por lo tanto, debe proporcionar diferentes nombres de constructor para compartir los datos subyacentes, C.

Si desea este factor, que puede hacer como los demás puestos han sugerido y datos por coeficientes de salida en constructores únicos para cada tipo de datos

data A = CForA C | Z 
data B = CForB C 

data C = X | Y 
Cuestiones relacionadas