如何扩展 Haskell 类型的实例?
How to extend a Haskell type instance?
This 优秀的教程,定义了一个 YesNo
类型 class 和一个 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
class 约束一起工作:
instance (Num a) => YesNo a where
yesno 0 = False
yesno _ = True
但是这个实例定义没有编译错误:
Constraint is no smaller than the instance head
我知道 Num
的构造函数数量比 YesNo
class 多,因此出现错误。但是如何在不设置 UndecidableInstances
编译器标志的情况下解决这个问题?
正如 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
中的任何一个,以启用等式约束)。
None 这很有意义 无论如何 :类型 class 的要点是为实现看起来 [=38= 的类型创建实例]不同。如果你将其限制为数字类型,这就是你对这些实例所做的,那么你最好远离类型 classes 并只定义
yesno :: (Num n, Eq n) => n -> Bool
yesno = (/=0)
从这个意义上说:不可判定的实例有点代码味道;在写之前确保你真的 需要一个 class!
This 优秀的教程,定义了一个 YesNo
类型 class 和一个 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
class 约束一起工作:
instance (Num a) => YesNo a where
yesno 0 = False
yesno _ = True
但是这个实例定义没有编译错误:
Constraint is no smaller than the instance head
我知道 Num
的构造函数数量比 YesNo
class 多,因此出现错误。但是如何在不设置 UndecidableInstances
编译器标志的情况下解决这个问题?
正如 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
中的任何一个,以启用等式约束)。
None 这很有意义 无论如何 :类型 class 的要点是为实现看起来 [=38= 的类型创建实例]不同。如果你将其限制为数字类型,这就是你对这些实例所做的,那么你最好远离类型 classes 并只定义
yesno :: (Num n, Eq n) => n -> Bool
yesno = (/=0)
从这个意义上说:不可判定的实例有点代码味道;在写之前确保你真的 需要一个 class!