Illegal instance declaration for typeclass TF

懵懂的女人 提交于 2020-01-02 07:31:35

问题


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:

  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 know 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. This means 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 instance for 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.



来源:https://stackoverflow.com/questions/23910988/illegal-instance-declaration-for-typeclass-tf

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!