Scala Cats中Monad约束类型无法解析flatMap符号问题
Scala Cats中Monad约束下for推导式无法识别flatMap的问题
代码定义
trait Client[F[_]] { def fetchPossiblePairs(fetcher: Uri => F[List[String]]): F[List[Rate.Pair]] def fetchQuotes(currencyPairs: List[Rate.Pair], fetcher: Uri => F[List[QuoteDTO]]): F[List[Rate]] def convertRate(uri: Uri): F[List[QuoteDTO]] def symbols(uri: Uri): F[List[String]] } object Quotes { private def getCurrencyPairs[F[_]: Monad](client: Client[F], existingRates: Map[Rate.Pair, Rate]): F[List[Rate.Pair]] = if (existingRates.isEmpty) { client.fetchPossiblePairs(client.symbols) } else { existingRates.keySet.toList.pure[F] } private def updateCache(existingRates: Map[Rate.Pair, Rate], newRates: List[Rate]): Map[Rate.Pair, Rate] = newRates.foldRight(existingRates)((rate: Rate, acc: Map[Rate.Pair, Rate]) => acc.updated(rate.pair, rate)) }
可运行但不够优雅的实现
object Quotes { private def refreshRatesCache[F[_]: Monad](client: Client[F], existingRates: Map[Rate.Pair, Rate]): F[Map[Rate.Pair, Rate]] = { val currencyPairs1 = getCurrencyPairs(client, existingRates) Monad[F] .flatMap(currencyPairs1)(currencyPairs => client.fetchQuotes(currencyPairs, uri => client.convertRate(uri))) .map(quotes => updateCache(existingRates, quotes)) } }
问题
尝试改用Scala标准for推导式重写refreshRatesCache时,IDE提示“Cannot resolve symbol flatMap”,SBT编译报错“value flatMap is not a member of type parameter F[List[Rate.Pair]]”。
明明getCurrencyPairs返回F[List[Rate.Pair]],且F[_]带有Monad约束,理论上Scala应该能推断出F的Monad实例并找到flatMap方法,但实际却无法识别。使用版本:Scala 2.13.5、SBT 1.9.0、cats-core 2.5.0、cats-effects 2.4.1。
原因分析
问题出在没有导入Cats的Monad语法扩展。Scala的for推导式依赖于类型本身拥有flatMap和map方法,但Cats的Monad类型类是通过隐式扩展来为F[_]添加这些方法的,而非直接在F的定义中实现。
直接调用Monad[F].flatMap(...)是显式使用类型类实例,所以可以运行;但for推导式是语法糖,会被编译为对flatMap和map方法的调用,此时如果没有导入对应的语法扩展,编译器就找不到这些方法。
解决方案
导入Cats的Monad语法包,让编译器能够通过隐式扩展为F[_]添加flatMap和map方法:
// 导入必要的语法扩展 import cats.syntax.flatMap._ import cats.syntax.functor._ object Quotes { private def refreshRatesCache[F[_]: Monad](client: Client[F], existingRates: Map[Rate.Pair, Rate]): F[Map[Rate.Pair, Rate]] = { for { currencyPairs <- getCurrencyPairs(client, existingRates) quotes <- client.fetchQuotes(currencyPairs, uri => client.convertRate(uri)) } yield updateCache(existingRates, quotes) } }
也可以一次性导入所有Cats语法简化代码:
import cats.implicits._
内容的提问来源于stack exchange,提问作者shybovycha
相关产品推荐
相关产品推荐

