【问题标题】:How to extend a Haskell type instance?如何扩展 Haskell 类型的实例?
【发布时间】:2016-04-10 08:31:37
【问题描述】:

This 优秀教程,定义了一个YesNo 类型类和一个YesNo Int 实例如下:

class YesNo a where
   yesno :: a -> Bool

instance YesNo Int where
   yesno 0 = False
   yesno _ = True

这适用于显式类型声明,例如:

*Main> yesno (0::Int)
False

我想避免显式类型声明并使其与Num a 类约束一起使用:

instance (Num a) => YesNo a where
   yesno 0 = False
   yesno _ = True

但是这个实例定义没有编译并出现错误: Constraint is no smaller than the instance head

我了解Num 的构造函数数量比YesNo 类多,因此会出现错误。但是如何在不设置 UndecidableInstances 编译器标志的情况下解决这个问题?

【问题讨论】:

  • Num 暗示 Eq: class (Eq a, Show a) => Num a
  • base-4.5 以来,Eq a, Show aNum a 的约束已被删除
  • UndecidableInstances 是相当无害的。可能发生的最坏情况是您创建了一组陷入无限循环的实例。即使在这种情况下,只要编译器终止它的工作,你的程序在运行时就可以正常运行。
  • 你说“Num 有更多的构造函数[大概你的意思是“实例”而不是“构造函数”] 比YesNo 类和错误“。但这不是错误的正确表征——不是Num实例数更大,而是您要查找的实例中的类型 在结构上并不小,因此不清楚实例搜索是否会仅从句法参数终止。

标签: haskell ghci


【解决方案1】:

正如 chi 已经说过的,-XUndecidableInstances 并不是什么大问题——好吧,它比-XFlexibleInstances 更具侵入性;您应该在需要时考虑片刻——但这是完全安全。它永远不会成功编译不能按预期方式工作的代码。

也就是说,要实现让yesno 0 独立工作的目标,instance (Num a) -> YesNo a正确的选择。有了这个实例,0 应该具有的具体类型仍然完全模棱两可(Num a => a 不是具体类型)。因此,编译器必须使用 defaulting 进行跳转,而且默认的 Num 类型是 Integer。因此,要使其正常工作,声明该实例就足够了:

instance YesNo Integer where
   yesno = (/=0)

或者,如果您希望计算始终Int 中完成,您可以通过等式约束来实现:

instance (a ~ Int) => YesNo a where
   yesno = (/=0)

这个确实需要-XUndecidableInstances(加上完全无害的-XFlexibleInstances-XGADTs-XTypeFamilies,以启用等式约束)。

这些都没有多大意义无论如何:类型类的重点是为实现看起来不同的类型创建实例不同。如果您将其限制为数字类型,这就是您对这些实例所做的事情,那么您不妨完全远离类型类,只定义

yesno :: (Num n, Eq n) => n -> Bool
yesno = (/=0)

从这个意义上说:不可判定的实例有点代码味道;在写之前确保你真的需要一个类

【讨论】:

  • 其实FlexibleInstancesUndecidableInstances还差!灵活的实例并不总是像人们希望的那样容易地减少。
  • 好的...示例/链接?
猜你喜欢
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
  • 2016-08-12
  • 2010-12-14
  • 2011-10-16
  • 1970-01-01
  • 2016-12-05
  • 1970-01-01
相关资源
最近更新 更多