Scala中如何优雅扁平化两侧类型相同的F[Either[T, T]]结构
更简洁的实现方案
Scala标准库和Cats都提供了专门的语法糖处理这种Either[T, T]合并的场景,比手动写fold(identity, identity)语义更清晰:
- Scala 2.13+ 标准库原生支持
标准库的Either从2.13版本开始内置了merge方法,作用就是当左右两侧类型相同时,直接提取包裹的值,不需要手动写fold逻辑。你的方法可以简化为:
def foo[F[_]: Functor, T](eitherF: F[Either[T, T]]): F[T] = eitherF.map(_.merge)
- Cats库跨版本兼容实现
如果你的项目Scala版本低于2.13,只要引入Cats的either语法扩展,就可以使用同样的merge方法:
import cats.syntax.either._ // 导入Either扩展方法
语义和标准库的merge完全一致,不需要修改核心逻辑。
优化后的完整可运行示例:
import cats.Functor import cats.syntax.functor._ import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt import scala.language.higherKinds def foo[F[_]: Functor, T](eitherF: F[Either[T, T]]): F[T] = eitherF.map(_.merge) val flattenProcess = foo[Future, String](Future(Left[String, String]("left"))) .flatMap{ prev => println(prev) foo[Future, String](Future(Right[String, String]("right"))) } println(Await.result(flattenProcess, 10.seconds)) // 输出: // left // right
设计合理性判断
这种场景是否属于Either的不合理使用,取决于你的业务逻辑:
- 如果你在生成
Either[T, T]的上游逻辑中,确实需要区分左右分支做不同的处理,只是到当前阶段不需要再区分分支属性,那这种用法完全合理,属于正常的业务流程分支收敛。 - 如果你上游逻辑也不需要区分左右分支,从一开始就能直接得到
T类型的值,那确实属于过度使用Either,完全可以去掉不必要的Either包装,简化整个链路的类型。
内容的提问来源于stack exchange,提问作者Boris Azanov
相关产品推荐
相关产品推荐

