2010-01-24 18 views
17

estoy leyendo Real World Haskell Pg 151, y he mirado en el siguiente pasaje de más de una hora:¿Cuál es el efecto de escribir sinónimos en instancias de clases de tipos? ¿Qué hace el pragma TypeSynonymInstances en GHC?

Recordemos que String es un sinónimo de [Char], que a su vez es el tipo [a] donde Char se sustituye por el parámetro tipo a. De acuerdo con las reglas de Haskell 98 , no se nos permite suministrar un tipo en lugar de un parámetro de tipo cuando escribimos una instancia. En otras palabras, sería legal para nosotros escribir una instancia para [a], pero no para [Char]. 16 comentarios 5335

Simplemente no se hunde en. Mirando el the (free not pirated) copy of RWH chapter 6veo un montón de otras personas realmente sufren con esto. Todavía no lo entiendo por los comentarios ...

En primer lugar, todo esto me confunde, así que si siente que puede explicar algo sobre este pasaje, o TypeSynonymInstances, por favor.

Aquí está mi problema:

  • Int es un constructor de datos
  • String es un constructor de datos Ysinónimo de tipo

ahora no puedo responde estas preguntas:

  1. ¿Por qué un sinónimo de tipo excluye el hecho de hacer que el tipo sea miembro de una clase de tipo (estoy buscando alguna razón que probablemente se relacione con la compilación o implimentation de un sinónimo de tipo)?
  2. ¿Por qué los diseñadores del lenguaje, no quieren esta sintaxis (estoy pidiendo razonamiento no extensa teoría o símbolos matemáticos Unicode).
  3. Veo esta línea "el tipo [a] donde Char se sustituye por el parámetro de tipo a", y quiero saber por qué no puedo sustituirlo por este "el tipo a donde se sustituye Int por el parámetro de tipo a ".

¡Gracias!

+1

En mi propio código, he encontrado que el uso de las instancias de sinónimos significa que realmente quiero usar 'newtype' (y tal vez derivación de tipo nuevo generalizada) en lugar de' tipo'. Si no está familiarizado con la diferencia entre 'type' y' newtype', ese debería ser su primer paso. – jrockway

Respuesta

27

Creo que parte del problema es que dos, en gran parte sin relación, las restricciones están en juego:

  • No sinónimo de tipo casos significa que los casos sólo pueden ser cosas declaradas con data o newtype, no type. Esto prohíbe String, pero no [Char].
  • Ninguna instancia flexible significa que las instancias solo pueden mencionar un tipo que no es una variable, y solo ese tipo se puede usar como un constructor de tipo. Esto prohíbe Maybe Int y f Int, pero no Maybe a.

Aquí es lo que dice acerca GHCi Int, Char, y String:

data Char = GHC.Types.C# GHC.Prim.Char# 
data Int = GHC.Types.I# GHC.Prim.Int# 
type String = [Char] 

Int y Char son los dos tipos simples sin parámetros variables de tipo; no hay un constructor de tipos involucrado, por lo que puedes hacer instancias con ellos de forma bastante libre.

Cadena, sin embargo, falla en ambos cuenta. Es un sinónimo de tipo, que no está permitido, y también es un constructor de tipo aplicado a un no variable, es decir, el constructor de tipo de lista aplicado a Char.

Para la comparación, tenga en cuenta que [a], Maybe a y Either a b son todas válidas en los casos, pero [Int], Maybe [a] y Either String a, están prohibidos; ojalá ahora puedas ver por qué.

En cuanto a sus preguntas directas, no sé cuáles fueron las motivaciones originales para diseñar el idioma de esa manera, y no estoy de ninguna manera calificado para hacer declaraciones autorizadas sobre "mejores prácticas", pero para mi personal codificación realmente no duda en utilizar estos pragmas:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
{-# LANGUAGE EmptyDataDecls #-} 
{-# LANGUAGE TypeSynonymInstances #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE FlexibleContexts #-} 

siempre se puede ir a buscar a packages that use pragmas. Aparentemente, las instancias flexibles obtienen una buena cantidad de uso, y de paquetes "respetables" (hay un par de visitas en la fuente de Parsec, por ejemplo).

+0

Siento como si todavía no entendiera totalmente esta respuesta.Pero está tan bien escrito que quiero convertirlo en la respuesta que elegí, aunque creo que la pieza de práctica fue esencial para la pregunta compuesta de manera excesiva, por lo que simplemente reformularé la pregunta sin ella. –

+0

Bueno, incluso si realmente no puedo hablar sobre las mejores prácticas, ver lo que otras personas están haciendo podría ayudar; Acabo de añadir un poco sobre eso la respuesta. –

+0

@ C.A.McCann: en la línea "Esto es lo que dice GHCi sobre Int, Char y String", ¿qué escribió en el indicador de GHCi para obtener esas respuestas? –

9

En realidad, ni Int ni String son constructores de datos. Es decir, no se puede utilizar para crear un valor de

> (Int 42, String "bob") 
<interactive>:1:1: Not in scope: data constructor `Int' 
<interactive>:1:9: Not in scope: data constructor `String' 

Int nombra una nueva, distinta, algebraica de tipos de datos. String es un "sinónimo de tipo", o alias, para el tipo ya existente: [Char]. El problema es que Haskell 98 dice que no puede usar un sinónimo de tipo en una declaración de instancia.

No puedo decir por qué los autores del informe Haskell 98 eligen restringir los sinónimos de tipo en este caso. Hay un gran número de restricciones sobre ellos. Por ejemplo, no se pueden aplicar parcialmente (si toman argumentos de tipo). Creo que hay una pista al final de §4.2.2:

Sinónimos de tipo son un cómodo, pero estrictamente sintáctica, el mecanismo para hacer las firmas de tipos más legible. Un sinónimo y su definición son completamente intercambiables, excepto en el tipo de instancia de una declaración de instancia (Sección 4.3.2).

Presumiblemente, había un enfoque para la compilación de programas, por lo que esta intercambiabilidad sintáctica habría causado problemas para las instancias. Tal vez tiene que ver con el aspecto notable de las instancias que se escapan de los paquetes ...

En cuanto a su última pregunta, creo que la explicación se confunde dos cosas: 1) String es un sinónimo de tipo [Char], que es a su vez una especialización del tipo más general [a] y 2) que incluso sin el sinónimo, [Char] no se puede utilizar en el encabezado de una instancia.

Este segundo problema no tiene nada que ver con los sinónimos de tipo, pero los encabezados de instancia deben tener todos los parámetros de tipo para el tipo constructor sean variables, no tipos concretos. Es decir, no puede definir instancias separadas para [Int] y [Char] para alguna clase, solo puede definir instancias [a]. (Recuerde que, a pesar de la sintaxis conveniente, [] es un constructor de tipos, y lo que está dentro es el parámetro de tipo)

De nuevo, no sé por qué el informe restringe estos, pero sospecho que también tiene que ver con estrategia de compilación. Dado que la estrategia de compilación de GHC para instancias puede manejar esto, puede relajar esta restricción en GHC a través del -XFlexibleInstances.

Finalmente, he visto ambas extensiones activadas con bastante código, pero tal vez alguien con más experiencia en Haskell pueda evaluar si son "mejores prácticas" o no.

+0

Otra excelente redacción, muchas gracias, su conocimiento es una ventaja para StackOverflow. Pregunta rápida: 'es decir, puedes definir instancias separadas para [Int] y [Char] para alguna clase', se supone que debe leer' can', o 'can't', porque parece entrar en conflicto con el resto del párrafo. Creo que [Int], y [Char], son tipos concretos, lo que significaría que no funcionarían como instancias de clases de tipos. –

+0

¡buena captura, corregida! – MtnViewMark

1

Haskell cabeza 98

  1. una instancia debe tener la forma C (T u1 ... uk), donde T es un constructor tipo definido por una declaración de datos o newtype (ver TypeSynonymInstances) y la ui son variables de tipo distintas, y
  2. cada aserción en el contexto debe tener la forma C 'v, donde v es una de las ui.

Por lo tanto, es válido utilizar instance ClassName TypeConstructor where y TypeConstructor MUST ser tal que Int, Double o [a], asegúrese de que sólo puede estar implicado un constructor de tipos !!

BTW, [] es de tipo constructor, por lo [TypeConstructor] no se puede utilizar pero [NewType] y [TypeVariable] se admiten.

Esta es una restricción es Haskell, y podemos evitarlo habilitando FlexibleInstances.

Cuestiones relacionadas