为何ext1Q对Maybe类型有效却对Either类型无效?
ext1Q对Maybe有效但对Either无效的原因及解决方法
我希望测试记录中的所有字段是否都使用指定的同一个构造器。ext1Q对Maybe类型有效,但对Either类型无效,这是为什么?
代码示例
{-# LANGUAGE DeriveDataTypeable #-} import Data.Maybe (isNothing, isJust) import Data.Either (isLeft, isRight) import Data.Data (Data, gmapQ) import Data.Generics.Aliases (ext1Q) allNothing :: (Data d) => d -> Bool allNothing = and . gmapQ (const True `ext1Q` isNothing) allJust :: (Data d) => d -> Bool allJust = and . gmapQ (const True `ext1Q` isJust) allLeft :: (Data d) => d -> Bool allLeft = and . gmapQ (const True `ext1Q` isLeft)
编译错误信息
CheckIfAllFieldsAreNothing.hs:18:35: error: • Could not deduce (base-4.16.3.0:Data.Typeable.Internal.Typeable a0) arising from a use of ‘ext1Q’ from the context: Data d bound by the type signature for: allLeft :: forall d. Data d => d -> Bool at CheckIfAllFieldsAreNothing.hs:17:1-32 or from: Data d1 bound by a type expected by the context: forall d1. Data d1 => d1 -> Bool at CheckIfAllFieldsAreNothing.hs:18:23-49 The type variable ‘a0’ is ambiguous • In the first argument of ‘gmapQ’, namely ‘(const True `ext1Q` isLeft)’ In the second argument of ‘(.)’, namely ‘gmapQ (const True `ext1Q` isLeft)’ In the expression: and . gmapQ (const True `ext1Q` isLeft) | 18 | allLeft = and . gmapQ (const True `ext1Q` isLeft)
尝试解决的困惑
我尝试解决时发现,由于没有“类型类”应用,无法添加Typeable (Either a b)约束。
原因分析
问题核心是ext1Q的设计目标与Either的类型构造器阶数不匹配:
ext1Q是专门适配一元类型构造器的扩展函数(即仅接受1个类型参数的构造器,比如Maybe :: * -> *),它要求第二个参数的输入类型是一元构造器应用后的具体类型(如Maybe Int)。isNothing :: Maybe a -> Bool完全符合这个要求,类型推断可以顺利确定参数类型,因此能正常工作。- 而
Either是二元类型构造器(需要2个类型参数,Either :: * -> * -> *),isLeft :: Either a b -> Bool的输入类型包含两个未确定的类型参数a和b,ext1Q无法推断出明确的类型,最终触发“类型变量歧义”的编译错误。
解决方法
使用Data.Generics.Aliases中专门针对二元类型构造器的ext2Q函数替代ext1Q,修改allLeft的定义:
import Data.Generics.Aliases (ext1Q, ext2Q) -- 新增ext2Q导入 allLeft :: (Data d) => d -> Bool allLeft = and . gmapQ (const True `ext2Q` isLeft)
同理,allRight也可以用ext2Q实现:
allRight :: (Data d) => d -> Bool allRight = and . gmapQ (const True `ext2Q` isRight)
ext2Q的类型签名为(Data a, Typeable b, Typeable c) => (a -> r) -> (b c -> r) -> a -> r,专门适配二元类型构造器的实例类型(如Either a b),能正确处理包含两个类型参数的情况,消除类型歧义。
内容的提问来源于stack exchange,提问作者Johnny Liao
相关产品推荐
相关产品推荐

