英文:
Cannot resolve symbol flatMap on Monad constrainted type
问题
Here's the translated code without the parts you requested not to translate:
我一直在使用Scala Cats,最终陷入了隐式(implicits)和类型约束(type constraints)的困境。
假设以下定义:
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))
}
}
要使其更符合传统,使用`for`推导,IDE显示错误“无法解析符号flatMap”:
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)
// ^
// |--- 无法解析符号flatMap
//
rates <- client.fetchQuotes(currencyPairs, uri => client.convertRate(uri))
} yield updateCache(existingRates, rates)
}
而SBT显示稍有不同的错误:
value flatMap is not a member of type parameter F[List[Rate.Pair]]
我理解这两种实现是相同的,只是使用了不同的语法糖。
`getCurrencyPairs[F[_]: Monad](...)`返回一个包装的`List`,`F[List[Rate.Pair]]`,鉴于对`F[_]: Monad`的类型约束,我假设Scala应该能够推断出`F extends Monad`并找到所有`Monad`的方法(包括`flatMap`)。
整个`for`推导都在相同的`F[_]`上下文中工作,因此似乎不是类型不匹配的问题。
我尝试展开隐式和`for`推导,因此得到了这个不太美观的实现:
```scala
Monad[F]
.flatMap
(getCurrencyPairs(client, existingRates))
(currencyPairs => client.fetchQuotes(currencyPairs, uri => client.convertRate(uri)))
.map(quotes => updateCache(existingRates, quotes))
在我看来,拥有(implicit m: Monad[F[_]])
并没有太多意义(至少我找不到正确的用法)。
我对Scala的隐式并不是很精通,但似乎我的代码有问题。我错在哪里?
我使用的版本是:
- Scala
2.13.5
- SBT
1.9.0
- cats-core
2.5.0
- cats-effects
2.4.1
<details>
<summary>英文:</summary>
I've been playing with scala cats and ended up stuck with implicits and type constraints.
Assume the following definitions:
```scala
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))
}
The following implementation works, but looks ugly and not really up to the standards:
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))
}
}
Making it more conventional, using the for
comprehension, IDE shows an error Cannot resolve symbol flatMap
:
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)
// ^
// |--- Cannot resolve symbol flatMap
//
rates <- client.fetchQuotes(currencyPairs, uri => client.convertRate(uri))
} yield updateCache(existingRates, rates)
}
And SBT shows a slightly different error:
value flatMap is not a member of type parameter F[List[Rate.Pair]]
My understanding is that the two implementations are the same, just using the different syntactic sugar.
getCurrencyPairs[F[_]: Monad](...)
returns a wrapped List
, F[List[Rate.Pair]]
, and given the type constraint on F[_]: Monad
I assume Scala should be able to infer F extends Monad
and find all the Monad
's methods (including flatMap
).
The entire for
comprehension works within the same context of F[_]
, hence seems it is not the type mismatch.
I tried to expand the implicits and for
comprehension, hence arriving at this ugly implementation:
Monad[F]
.flatMap
(getCurrencyPairs(client, existingRates))
(currencyPairs => client.fetchQuotes(currencyPairs, uri => client.convertRate(uri)))
.map(quotes => updateCache(existingRates, quotes))
It does not make a lot of sense to have (implicit m: Monad[F[_]])
, in my opinion (at least I could not come up with the correct usage for it).
I am not really proficient with Scala implicits, but seems like there is something wrong with my code. Where am I wrong?
The versions I'm using are:
- Scala
2.13.5
- SBT
1.9.0
- cats-core
2.5.0
- cats-effects
2.4.1
答案1
得分: 2
这几乎可以肯定是因为缺少提供该方法的导入。
添加 import cats.syntax.all._
英文:
This is almost certainly due to missing the import that provides the method.
Add import cats.syntax.all._
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论