Dado :
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 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
String
es 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)
porqueChar
no 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 latoFoo
función se comporte de manera diferente sia
es un Char?-XOverlappingInstances
que 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:
-XTypeSynonymInstances
que le permite usar sinoyms de tipo (como
String
para[Char]
), y:-XFlexibleInstances
que eliminan las restricciones sobre los tipos de instancia que tienen la forma
T a b ..
donde los parámetros son variables de tipo. La-XFlexibleInstances
bandera 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 = listToFoo
Vé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