2011-05-09 13 views
80

Dado:¿Por qué no puedo hacer que String sea una instancia de una clase de tipo?

data Foo = 
    FooString String 
    … 

class Fooable a where --(is this a good way to name this?) 
    toFoo :: a -> Foo 

Quiero hacer String una instancia de Fooable:

instance Fooable String where 
    toFoo = FooString 

GHC continuación, se queja:

Illegal instance declaration for `Fooable String' 
    (All instance types must be of the form (T t1 ... tn) 
    where T is not a synonym. 
    Use -XTypeSynonymInstances if you want to disable this.) 
In the instance declaration for `Fooable String' 

Si en vez utilizo [Char]:

instance Fooable [Char] where 
    toFoo = FooString 

GHC se queja:

Illegal instance declaration for `Fooable [Char]' 
    (All instance types must be of the form (T a1 ... an) 
    where a1 ... an are type *variables*, 
    and each type variable appears at most once in the instance head. 
    Use -XFlexibleInstances if you want to disable this.) 
In the instance declaration for `Fooable [Char]' 

Pregunta:

  • ¿Por qué no puedo realizar cuerda y una instancia de clase de tipos?
  • GHC parece dispuesto a dejarme salir con esto si agrego una bandera extra. ¿Es esta una buena idea?
+6

Este es el tipo de preguntas que upvote y marcar como favorito porque de lo contrario Sé que en una futuro lo estaría preguntando;) –

+3

En cuanto a la bandera adicional: es probable que sea una buena idea, siempre y cuando confíes en GHC y entiendas lo que hace la bandera. [Yesod] (http://www.yesodweb.com/) me viene a la mente: lo alienta a usar siempre el pragma OverloadedStrings cuando escribe aplicaciones Yesod, y QuasiQuotes es una necesidad para las reglas de enrutamiento de Yesod. Tenga en cuenta que en lugar de un indicador en tiempo de compilación, también puede poner '{- # LANGUAGE FlexibleInstances # -}' (o cualquier otro pragma) en la parte superior de su archivo .hs. –

Respuesta

57

Esto se debe a String es sólo un alias de tipo para [Char], que es simplemente la aplicación del constructor de tipos [] del tipo Char, por lo que este podría ser de la forma ([] Char). que no tiene el formato (T a1 .. an) porque Char no es una variable de tipo.

El motivo de esta restricción es evitar la superposición de instancias. Por ejemplo, digamos que tenía un instance Fooable [Char], y luego alguien apareció y definió un instance Fooable [a]. Ahora el compilador no podrá determinar cuál quiere usar y le dará un error.

Al usar -XFlexibleInstances, básicamente le está prometiendo al compilador que no definirá ninguna de esas instancias.

Dependiendo de lo que estamos tratando de lograr, tal vez sería mejor para definir una envoltura:

newtype Wrapper = Wrapper String 
instance Fooable Wrapper where 
    ... 
+4

Digamos por razones de argumento que yo también quería 'instancia Fooable [a]' también. ¿Hay alguna manera de hacer que la función 'toFoo' se comporte de manera diferente si' a' es un Char? –

+7

@John: existe una extensión '-XOverlappingInstances' que permitirá esto y seleccionará la instancia más específica. [Consulte la guía del usuario de GHC para obtener más información] (http://www.haskell.org/ghc/docs/latest/html/users_guide/type-class-extensions.html#instance-overlap). – hammar

17

Usted está ejecutando en dos clases de tipos de limitaciones Haskell98 clásicos:

  • que no permitir sinónimos de tipos en casos
  • que no permiten tipos anidados que no lo hacen a su vez contienen las variables de tipo.

Estas restricciones onerosas son levantadas por dos extensiones de lenguaje:

  • -XTypeSynonymInstances

que le permite utilizar synoyms tipo (como String para [Char]), y:

  • -XFlexibleInstances

que levantan las restricciones en los tipos de instancia que tienen el formato T a b .. donde los parámetros son variables de tipo. El indicador -XFlexibleInstances permite al jefe de la declaración de instancia mencionar tipos anidados arbitrarios.

Tenga en cuenta que el levantamiento de estas restricciones a veces puede llevar a overlapping instances, momento en el que podría necesitarse una extensión de idioma adicional para resolver la ambigüedad, permitiendo a GHC elegir una instancia para usted.


Referencias::

2

añadiendo a estas respuestas, si no se siente cómodo con el levantamiento de las restricciones, puede haber casos en que podría tener sentido envolver su cadena en un nuevo tipo, que puede ser una instancia de una clase. La compensación sería una fealdad potencial, tener que envolver y desenvolver en su código.

4

FlexibleInstances no son una buena respuesta en la mayoría de los casos. mejores alternativas están terminando la cadena en un newtype o introducir una clase de ayuda, así:

class Element a where 
    listToFoo :: [a] -> Foo 

instance Element Char where 
    listToFoo = FooString 

instance Element a => Fooable [a] where 
    toFoo = listToFoo 

Ver también: http://www.haskell.org/haskellwiki/List_instance

Cuestiones relacionadas