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 where valid = id lequiv f g = f == g instance TF p => TF (Bool -> p) where 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
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:
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 idwill not typecheck without further annotations.
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 idwill typecheck; on the other hand, it also means you cannot declare instances for any other argument types.
Add a typeclass. In fact, the only thing you really care about is that you can list all the inhabitants of
Boolin 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
Bool(which, luckily, is already available for you in the
universepackage). 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
Finiteinstances for them.