如何安全优化Scala For Comprehension中的可选值调用,返回Option[T]
安全优化Scala For Comprehension中的Option处理
你的代码里的product.get和warehouseId.get都是不安全调用,一旦对应的Option为None就会抛出异常。要实现"任意环节出现None就返回None"的需求,需要结合ConnectionIO和Option的嵌套上下文处理,以下是两种可行方案:
方案一:使用OptionT(推荐,代码更简洁)
借助Cats的OptionT monad transformer,可将ConnectionIO[Option[_]]这类嵌套类型统一为单一上下文,直接用for comprehension处理,自动完成None的短路逻辑:
import cats.data.OptionT import cats.implicits._ case class ProductView(product: Product, stores: List[Store], warehouse: Option[Warehouse]) def loadView(...): ConnectionIO[Option[ProductView]] = ( for { // 将ConnectionIO[Option[Product]]包装为OptionT,自动处理None分支 product <- OptionT(getProductById(id)) // 提取product.warehouseId的Option,转为OptionT上下文 warehouseId <- OptionT.fromOption[ConnectionIO](product.warehouseId) // 加载仓库,同样包装为OptionT warehouse <- OptionT(getWarehouse(warehouseId.id)) // 将ConnectionIO[List[Store]]转为OptionT(List不会为None,直接包装为Some) stores <- OptionT.liftF(loadStores(...)) } yield ProductView(product, stores, warehouse) ).value // 将OptionT转回ConnectionIO[Option[ProductView]]
方案二:手动嵌套flatMap处理
若不想引入额外transformer,可通过flatMap手动处理每一层Option的分支:
case class ProductView(product: Product, stores: List[Store], warehouse: Option[Warehouse]) def loadView(...): ConnectionIO[Option[ProductView]] = getProductById(id).flatMap { case None => ConnectionIO.pure(None) case Some(product) => product.warehouseId match { case None => ConnectionIO.pure(None) case Some(warehouseId) => getWarehouse(warehouseId.id).flatMap { warehouseOpt => loadStores(...).map { stores => Some(ProductView(product, stores, warehouseOpt)) } } } }
你的尝试代码类型不匹配的原因
你写的product.map(p => p.warehouseId.map(id => getWarehouse(id)))最终类型是Option[Option[ConnectionIO[Option[Warehouse]]]],而for comprehension的每一步需要返回ConnectionIO[_]类型的值,这种多层嵌套的Option和IO无法适配上下文,因此出现类型错误。
内容的提问来源于stack exchange,提问作者Blankman
相关产品推荐
相关产品推荐

