Dado :
data Foo =
FooString String
…
class Fooable a where --(is this a good way to name this?)
toFoo :: a -> Foo
Eu quero fazer String
uma instância de Fooable
:
instance Fooable String where
toFoo = FooString
GHC então reclama:
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'
Se, em vez disso, eu usar [Char]
:
instance Fooable [Char] where
toFoo = FooString
GHC reclama:
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]'
Questão :
- Por que não posso fazer String e instância de uma typeclass?
- GHC parece disposto a me deixar escapar com isso se eu adicionar um sinalizador extra. isso é uma boa ideia?
haskell
ghc
typeclass
type-systems
John F. Miller
fonte
fonte
{-# LANGUAGE FlexibleInstances #-}
(ou qualquer outro pragma) no topo do seu arquivo .hs.Respostas:
Isso ocorre porque
String
é apenas um alias de tipo para[Char]
, que é apenas a aplicação do construtor de tipo[]
no tipoChar
, então isso seria do formulário([] Char)
. que não tem a forma(T a1 .. an)
porqueChar
não é uma variável de tipo.O motivo dessa restrição é evitar a sobreposição de instâncias. Por exemplo, digamos que você tivesse um
instance Fooable [Char]
e, mais tarde, alguém apareceu e definiu uminstance Fooable [a]
. Agora, o compilador não será capaz de descobrir qual você deseja usar e apresentará um erro.Ao usar
-XFlexibleInstances
, você basicamente promete ao compilador que não definirá nenhuma dessas instâncias.Dependendo do que você está tentando realizar, pode ser melhor definir um wrapper:
newtype Wrapper = Wrapper String instance Fooable Wrapper where ...
fonte
instance Fooable [a]
. Existe uma maneira de fazer atoFoo
função se comportar de maneira diferente sea
for um Char?-XOverlappingInstances
que permite isso e escolhe a instância mais específica. Consulte o guia do usuário do GHC para obter detalhes .Você está enfrentando duas limitações de typeclasses Haskell98 clássicas:
Essas restrições onerosas são eliminadas por duas extensões de linguagem:
-XTypeSynonymInstances
que permite usar sinônimos de tipo (como
String
para[Char]
) e:-XFlexibleInstances
que levantam as restrições sobre os tipos de instância sendo da forma em
T a b ..
que os parâmetros são variáveis de tipo. O-XFlexibleInstances
sinalizador permite que o cabeçalho da declaração da instância mencione tipos aninhados arbitrários.Observe que o levantamento dessas restrições às vezes pode levar à sobreposição de instâncias ; nesse ponto, uma extensão de linguagem adicional pode ser necessária para resolver a ambigüidade, permitindo que o GHC escolha uma instância para você.
Referências ::
fonte
FlexibleInstances não é uma boa resposta na maioria dos casos. Melhores alternativas são envolver a String em um novo tipo ou introduzir uma classe auxiliar como esta:
class Element a where listToFoo :: [a] -> Foo instance Element Char where listToFoo = FooString instance Element a => Fooable [a] where toFoo = listToFoo
Veja também: http://www.haskell.org/haskellwiki/List_instance
fonte
Somando-se a essas respostas, se você não se sentir confortável em suspender as restrições, pode haver casos em que faria sentido envolver sua String em um novo tipo, que pode ser uma instância de uma classe. A compensação seria a feiura potencial, ter que embrulhar e desembrulhar em seu código.
fonte