Haskell使用Quantified Constraints定义高阶类型类实例报错咨询
问题根源
你定义的ExClass超类约束要求forall a. Monoid (t a),即对任意无限制的类型a,t a都必须实现Monoid,但你的ExType仅在a满足Num约束时才是Monoid,二者的约束范围不匹配。
你写的两个实例声明本质都无法满足前提:
instance (forall a. Num a) => ExClass ExType要求所有类型都属于Num类型类,显然不成立(比如Bool就不是Num的实例)instance (forall a. Monoid(ExType a)) => ExClass ExType要求所有类型a都能让ExType a成为Monoid,同样不成立
这就是为什么实例能通过编译,但调用时始终无法满足约束的原因。
可行解决方案
方案1:收窄超类约束的适用范围(最推荐)
因为你的exFunc和exFunc2都只作用于t (Complex a)类型,且exFunc2已经有RealFloat a的约束,而RealFloat a天然能推导出Complex a属于Num类型类,刚好满足ExType的Monoid约束要求。可以直接调整ExClass的超类约束:
{-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE UndecidableInstances #-} import Data.Complex import Data.Complex (RealFloat, magnitude) class (forall a. RealFloat a => Monoid (t (Complex a))) => ExClass t where exFunc :: RealFloat a => [t (Complex a)] -> t (Complex a) exFunc = mconcat -- 默认实现正常生效 exFunc2 :: RealFloat a => t (Complex a) -> a
对应的ExType实例不需要额外加约束:
newtype ExType a = ExType a instance (Num a) => Semigroup (ExType a) where ExType a <> ExType b = ExType (a * b) instance (Num a) => Monoid (ExType a) where mempty = ExType 1 instance ExClass ExType where exFunc2 (ExType a) = magnitude a
此时你的调用代码可以正常运行:
x = ExType 2 :: ExType (Complex Double) func = exFunc2 x -- 正常返回2.0
方案2:用关联约束保留通用性
如果你需要ExClass支持更通用的Monoid约束条件,可以开启ConstraintKinds扩展,把Monoid所需的约束定义为类型类的关联类型:
{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE UndecidableInstances #-} import Data.Complex import GHC.Exts (Constraint) class ExClass t where -- 关联约束:指定t a实现Monoid需要的前提 type MonoidReq t a :: Constraint type MonoidReq t a = () -- 默认无额外约束 exFunc :: (MonoidReq t (Complex a), Monoid (t (Complex a))) => [t (Complex a)] -> t (Complex a) exFunc = mconcat exFunc2 :: RealFloat a => t (Complex a) -> a -- ExType的实例 instance ExClass ExType where type MonoidReq ExType a = Num a -- 声明ExType实现Monoid需要a是Num exFunc2 (ExType a) = magnitude a
这种写法同样可以正常运行调用代码,还能适配更多t的不同Monoid约束需求。
内容的提问来源于stack exchange,提问作者w41g87
相关产品推荐
相关产品推荐

