Dado :
data Foo =
  FooString String
  …
class Fooable a where --(is this a good way to name this?)
  toFoo :: a -> Foo
Quiero hacer Stringuna 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 una cadena y una instancia de una clase de tipo?
- GHC parece dispuesto a dejarme salirse con la suya si agrego una bandera adicional. ¿Es esta una buena idea?
                    
                        haskell
                                ghc
                                typeclass
                                type-systems
                                
                    
                    
                        John F. Miller
fuente
                
                fuente

{-# LANGUAGE FlexibleInstances #-}(o cualquier otro pragma) en la parte superior de su archivo .hs.Respuestas:
Esto se debe a que
Stringes solo un alias de tipo para[Char], que es solo la aplicación del constructor de tipo[]en el tipoChar, por lo que sería de la forma([] Char). que no es de la forma(T a1 .. an)porqueCharno es una variable de tipo.El motivo de esta restricción es evitar la superposición de instancias. Por ejemplo, digamos que tuviste un
instance Fooable [Char], y luego alguien vino y definió uninstance Fooable [a]. Ahora el compilador no podrá averiguar cuál quería usar y le dará un error.Al usar
-XFlexibleInstances, básicamente le promete al compilador que no definirá ninguna de esas instancias.Dependiendo de lo que esté tratando de lograr, podría ser mejor definir un contenedor:
newtype Wrapper = Wrapper String instance Fooable Wrapper where ...fuente
instance Fooable [a]. ¿Hay alguna manera de hacer que latoFoofunción se comporte de manera diferente siaes un Char?-XOverlappingInstancesque permite esto y elige la instancia más específica. Consulte la guía del usuario de GHC para obtener más detalles .Te encuentras con dos limitaciones de las clases de tipos clásicas de Haskell98:
Estas onerosas restricciones se eliminan mediante dos extensiones de idioma:
-XTypeSynonymInstancesque le permite usar sinoyms de tipo (como
Stringpara[Char]), y:-XFlexibleInstancesque eliminan las restricciones sobre los tipos de instancia que tienen la forma
T a b ..donde los parámetros son variables de tipo. La-XFlexibleInstancesbandera permite que el encabezado de la declaración de instancia mencione tipos anidados arbitrarios.Tenga en cuenta que la eliminación de estas restricciones a veces puede llevar a instancias superpuestas , momento en el cual, es posible que se necesite una extensión de idioma adicional para resolver la ambigüedad, lo que permite que GHC elija una instancia por usted.
Referencias ::
fuente
FlexibleInstances no es una buena respuesta en la mayoría de los casos. Mejores alternativas son envolver el String en un nuevo tipo o introducir una clase auxiliar como esta:
class Element a where listToFoo :: [a] -> Foo instance Element Char where listToFoo = FooString instance Element a => Fooable [a] where toFoo = listToFooVéase también: http://www.haskell.org/haskellwiki/List_instance
fuente
Además de estas respuestas, si no se siente cómodo levantando las restricciones, puede haber casos en los que tenga sentido envolver su String en un nuevo tipo, que puede ser una instancia de una clase. La compensación sería una posible fealdad, tener que envolver y desenvolver su código.
fuente