Declaración de instancia ilegal para la clase de tipo TF

I am having a problem declaring an instance of the following typeclass. I tried to follow the advice in the error message from the ghci compiler but still cannot get the code to compile. Any help would be appreciated.

class TF p where 
  valid :: p -> Bool
  lequiv :: p -> p -> Bool

instance TF Bool
  valid  = id
  lequiv f g = f == g

instance TF p => TF (Bool -> p)
  valid f = valid (f True) && valid (f False)
  lequiv f g = (f True) `lequiv` (g True)
               && (f False) `lequiv` (g False)

El error que recibo es:

Illegal instance declaration for ‘TF (Bool -> p)’
 (All instance types must be of the form (T a1 ... an)
  where a1 ... an are *distinct type variables*,
  and each type variable appears at most once in the instance head.
  Use FlexibleInstances if you want to disable this.)
  In the instance declaration for ‘TF (Bool -> p)’

preguntado el 28 de mayo de 14 a las 12:05

As the error message suggests, use FlexibleInstaces. To do that just put {-# LANGUAGE FlexibleInstances #-} en la parte superior de su archivo fuente. -

If I didn't want to disable this feature, what would I need to change in the instance declaration to comply with the Haskell specification? -

Again, as error message tells you, instance types must be of the form (T a1 ... an). In your case type is Bool -> p. aquí -> is type constructor and corresponds to T mientras Bool y p corresponde a a1 y a2. Es decir Bool -> p Se puede escribir como (->) Bool p. El problema aquí es que Bool is concrete type, but without FlexibleInstances it has to be type variable. So if you could make instance for a -> p instead that would work. -

Please note that you are not disabling a feature, but rather disabling a restriction. So, requiring FlexibleInstances actually asks GHC to enable a new feature, allowing more general instances. -

FlexibleInstances is one of the "very much ok" extensions. The restriction it removes is basically just there to make it easier to write a Haskell98 compiler, but from the user's point of view it's really more an arbitrary hindrance. -

1 Respuestas

The problem here is that you have a type constructor (->) applied to things that aren't type variables. There's a lot of ways you can deal with that:

  1. FlexibleInstances. This relaxes the assumption (made in the early days of Haskell, when it wasn't yet clear how difficult implementing type classes would be). This is not very controversial at all. On the other hand, it doesn't play that well with type inference: your instance will only be chosen when we sabes qué that we're supplying something of the shape Bool -> p -- and in particular something that's polymorphic in the first argument will not match that shape. So valid id will not typecheck without further annotations.
  2. TypeFamilies. This gives us (among other things) access to a constraint which demands that two particular types be equal. So with this extension, you could write

    instance (bool ~ Bool, TF p) => TF (bool -> p) where ...

    Now this matches whenever the thing we're supplying has shape bool -> p -- that is, any function at all -- and only after we have selected this instance does it check (in fact, enforce) that the argument type is Bool. Esto significa valid id will typecheck; on the other hand, it also means you cannot declare instances for any other argument types.

  3. Add a typeclass. In fact, the only thing you really care about is that you can list all the inhabitants of Bool in not too much time. So you could instead declare a typeclass, say, Finite, which you will instantiate at such types, and use it as the constraint on the argument type. Thus:

    instance (Finite arg, TF p) => TF (arg -> p) where
        valid f = all (valid . f) universe
        lequiv f g = all (\x -> f x `lequiv` g x) universe
        -- could also spell that lambda "liftA2 lequiv f g"

    Then you would want to provide a Finite instancia para Bool (which, luckily, is already available for you in the universe package). This is nice because it combines the strengths of the previous two approaches: this instance will be chosen as soon as we know the argument is a function, and you can declare instances for many argument types by adding Finite instances for them.

contestado el 28 de mayo de 14 a las 14:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.