问题
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)
The error I am getting is:
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)’
回答1:
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 know that we're supplying something of the shapeBool -> p
-- and in particular something that's polymorphic in the first argument will not match that shape. Sovalid id
will 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 writeinstance (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 isBool
. This meansvalid id
will 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
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
instance forBool
(which, luckily, is already available for you in theuniverse
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 addingFinite
instances for them.
来源:https://stackoverflow.com/questions/23910988/illegal-instance-declaration-for-typeclass-tf