Scalaz中带Success包装的/类型使用>>=运算符隐式解析失败原因问询
问题根因
这个问题本质是Scalaz的>>=运算符依赖的Bind类型类隐式解析逻辑,和你自定义的Example类型的结构不匹配导致的,核心有两个层面的原因:
1. 类型构造器的对齐逻辑差异
Scala原生的flatMap是\/类本身自带的成员方法,它的签名是:
def flatMap[R2](f: R1 => L \/ R2): L \/ R2
你在使用带Success包装的Example时,R1就是Success[A],入参函数f刚好符合Success[A] => Unit \/ Success[B]的要求,所以可以直接通过类型检查。
而Scalaz的>>=是基于通用Bind[M[_]]类型类的扩展方法,它要求M是单参数的类型构造器,并且M有对应的Bind实例:
- 当你定义
type Example[A] = Unit \/ A时,Example本质就是\/[Unit, _],Scalaz默认已经给所有\/[L, _]提供了Bind实例,隐式可以直接匹配到,所以>>=能正常使用。 - 当你改成
type Example[A] = Unit \/ Success[A]时,Example这个类型构造器相当于对\/[Unit, _]做了一层参数映射:把传入的A包一层Success再作为\/的右参数,Scalaz默认没有给这种套了一层的类型构造器提供Bind实例,自然没办法直接解析到>>=方法。
2. Unapply类型推导的局限性
从你贴的报错信息可以看到,编译器尝试用Unapply机制解构Example[A]的类型时,错误地把\/的左类型参数推导成了Nothing而不是Unit,导致匹配到的DisjunctionInstances1实例和实际类型不兼容。这是Scala 3下Scalaz的Unapply推导对嵌套类型参数的支持缺陷导致的:当\/的右参数不是裸类型而是套了一层case class时,Unapply无法正确推导出左参数的真实类型。
解决方案
你可以选其中一种方式解决:
- 直接给
Example类型手动声明Bind实例,之后就可以正常用>>=:
given Bind[Example] with override def bind[A, B](fa: Example[A])(f: A => Example[B]): Example[B] = fa.flatMap(s => f(s.exampleField)) override def map[A, B](fa: Example[A])(f: A => B): Example[B] = fa.map(s => Success(f(s.exampleField)))
- 调用
>>=时显式把a的类型转换成Unit \/ Success[A],绕过类型别名的推导干扰:
def doBind[A, B](a: Example[A])(f: Success[A] => Example[B]): Example[B] = (a: Unit \/ Success[A]) >>= f
内容的提问来源于stack exchange,提问作者Silvio Mayolo
相关产品推荐
相关产品推荐

