Scala Cats代码依赖withFilter是正常还是Bug?编译报错求助
withFilter错误 首先,我们来拆解你遇到的问题:当你在for推导式的生成器中给变量添加Int类型注解时,Scala编译器试图调用EitherT的withFilter方法,但cats.data.EitherT并没有提供这个方法,导致编译失败。
为什么会触发withFilter?
这不是Cats的Bug,而是Scala编译器对for推导式的翻译规则导致的。当你在for推导式中使用类型注解(比如i: Int)或者模式匹配时,编译器会默认认为你需要对元素进行“合法性检查”——确保提取出的值符合类型(或模式)要求。
具体来说,你的代码:
object Test { def exec[F[_]: cats.Monad] = for { i: Int <- cats.data.EitherT.fromEither[F](Right(0)) } yield i }
会被编译器自动翻译成类似下面的代码:
cats.data.EitherT.fromEither[F](Right(0)) .withFilter(i => i.isInstanceOf[Int]) .map(i => i.asInstanceOf[Int])
而普通的不带类型注解的i <- expr则直接翻译成expr.map(i => i),不会涉及withFilter调用。
为什么EitherT没有withFilter?
Cats的EitherT是基于Monad类型类实现的,而Monad类型类本身并不要求实现过滤(filter/withFilter)能力。过滤属于更上层的抽象,比如MonadFilter或FilterMonad。
EitherT的设计核心是处理“要么成功要么失败”的计算,过滤操作需要明确:当值不符合类型(或条件)时,应该返回什么错误?由于EitherT没有默认的错误值可以使用,所以它并没有提供withFilter方法——这是有意的设计,而非Bug。
解决方法
你有几种简单的方式可以解决这个问题:
去掉类型注解:
由于EitherT.fromEither[F](Right(0))的右值已经明确是Int,编译器可以自动推断出i的类型,完全不需要手动添加注解:object Test { def exec[F[_]: cats.Monad] = for { i <- cats.data.EitherT.fromEither[F](Right(0)) } yield i }显式转换(如果确实需要类型确认):
如果你出于某种原因必须明确类型,可以把类型检查放到yield块中,或者用map显式转换:object Test { def exec[F[_]: cats.Monad] = for { i <- cats.data.EitherT.fromEither[F](Right(0)) } yield i: Int }使用支持过滤的类型类(仅当需要过滤逻辑时):
如果你确实需要过滤逻辑,可以引入MonadFilter并为EitherT提供实例,但这需要你手动处理过滤失败时的错误值,比如:import cats.MonadFilter import cats.data.EitherT object Test { def exec[F[_]: MonadFilter] = for { i <- EitherT.fromEither[F](Right(0): Either[String, Int]) if i > 0 // 这里的if会触发withFilter,需MonadFilter实例 } yield i }不过这种方式在你的场景中完全没必要,因为值已经是
Int了。
内容的提问来源于stack exchange,提问作者isaias-b

