You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 07:22:43