简体   繁体   English

类型类TF的非法实例声明

[英]Illegal instance declaration for typeclass 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. 我试图遵循ghci编译器的错误消息中的建议,但仍然无法获取编译代码。 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)’

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 . 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). 这放松了这个假设(在Haskell的早期阶段,当时还不清楚实现类型类的难度)。 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. 另一方面,它在类型推断方面表现不佳:只有在我们知道我们提供的形状Bool -> p - 以及特别是第一个参数中具有多态性的东西时,才会选择您的实例。不符合那个形状。 So valid id will not typecheck without further annotations. 因此,如果没有进一步的注释, valid id将不会进行类型检查。
  2. TypeFamilies . 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 . 现在,只要我们提供的东西形状bool -> p - 即任何函数 - 并且只有在我们选择了这个实例后才会检查(实际上是强制执行)参数类型是Bool This means valid id will typecheck; 这意味着valid id会出现问题; 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. 事实上,你唯一真正关心的是你可以在不太多的时间内列出Bool所有居民。 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. 所以,你可以改为声明类型类,比如说Finite ,你将在这些类型的实例,并用作为参数的数据类型的约束。 Thus: 从而:

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

    Then you would want to provide a Finite instance for Bool (which, luckily, is already available for you in the universe package). 然后你想为Bool提供一个Finite实例(幸运的是,在universe包中已经可以使用了它)。 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. 这很好,因为它结合了前两种方法的优点:只要我们知道参数是一个函数,就会选择这个实例,并且可以通过为它们添加Finite实例来声明许多参数类型的实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM