haskell - reales - ¿Por qué no puedo hacer que String sea una instancia de una clase de letra?
string en haskell (4)
Además de estas respuestas, si no se siente cómodo con el levantamiento de las restricciones, puede haber casos en los que podría tener sentido ajustar 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.
Dado :
data Foo =
FooString String
…
class Fooable a where --(is this a good way to name this?)
toFoo :: a -> Foo
Quiero hacer de String
una instancia de Fooable
:
instance Fooable String where
toFoo = FooString
GHC luego 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 cambio, uso [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 hacer Cadena e instancia de una clase de letra?
- GHC parece dispuesto a dejarme salir con esto si agrego una bandera extra. ¿Es esta una buena idea?
Esto se debe a que String
es solo un alias de tipo para [Char]
, que es solo la aplicación del constructor de tipo []
en el tipo Char
, por lo que sería de la forma ([] Char)
. que no es de la forma (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, supongamos que tienes una instance Fooable [Char]
, y luego alguien apareció y definió una 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 intente lograr, podría ser mejor definir un contenedor:
newtype Wrapper = Wrapper String
instance Fooable Wrapper where
...
Las instancias flexibles no son una buena respuesta en la mayoría de los casos. Mejores alternativas son envolver el String en un newtype o introducir una clase de ayuda como sigue:
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
Te encuentras con dos limitaciones de las clases de tipos Haskell98 clásicos:
- deshabilitan el tipo de sinónimos en instancias
- deshabilitan los tipos anidados que, a su vez, no contienen variables de tipo.
Estas restricciones onerosas son levantadas por dos extensiones de idioma:
-
-XTypeSynonymInstances
que le permite usar synoyms tipo (como String
para [Char]
), y:
-
-XFlexibleInstances
que levantan las restricciones sobre los tipos de instancia que son de la forma T ab ..
donde los parámetros son variables de tipo. El -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 la superposición de instancias , en cuyo caso , podría necesitarse una extensión de lenguaje adicional para resolver la ambigüedad, permitiendo a GHC elegir una instancia para usted.
Referencias :
- Reglas relajadas para el jefe de la instancia , en la Guía del usuario de GHC.