[英]Recursive monad execution with cats
有一个简单的抽象查找服务,可以通过键检索值:
trait LookUp[F[_]] {
def read(key: String): F[Option[String]]
}
这个服务有一个用例,想法是给已实现的存储和累加器提供起始键,然后从数据库中请求值,如果结果为无,则停止并返回无,如果找到该值,则将其添加到累加器列表中并查看将下一个值作为上一个调用结果的键。 当检索到的值之前已经找到或没有检索到时,执行停止。 然后返回所有 acc 元素的字符串作为结果。
像这样试过:
def readWhileFound[F[_]: Monad](db: LookUp[F], acc: List[String]): F[Option[String]] = {
for{ result <- db.read(acc.head)} yield result match {
case Some(value) if(!acc.contains(value)) => readWhileFound(db, value::acc)
case _ => acc.mkstring("")
}
}
但是我无法正确获取类型,出现不匹配错误,例如:
found : F[cats.data.OptionT[[_]F[_],String]]
required: cats.data.OptionT[F,String]
方法二:
def readWhileFound[F[_]: Monad](key: String, db: LookUp[F])(implicit m: Monad[F]): F[Option[String]] = {
m.tailRecM((Option(key), List.empty[String])) { case (currentK, accum) =>
currentK match {
case Some(value) if(!accum.contains(value)) => m.pure(Left((db.read(value), value :: accum)))
case _ => m.pure(Right(Some(accum.mkString(""))))
}
}
}
获取编译器错误:
(Found) F[Option[String]]
required: Option[String]
case Some(value) if(!accum.contains(value)) => m.pure(Left((db.read(value), value :: accum)))
看起来 db.read(value) 应该以某种方式从 F 中解开
这看起来是fs2 的一个很好的用例:
你应该能够做这样的事情:
import fs2.Stream
def readWhileFound[F[_]: Concurrent](db: LookUp[F])(initialKey: String): F[List[String] =
Stream.unfoldEval(initialKey) { currentKey =>
db.read(key = currentKey).map(k => (k, k))
}.compile.toList
您在第一个实现中match
了错误的表达式。 您应该match
result
,而不是整个 for-comprehension 。 下面的实现应该做你所追求的。
def readWhileFound[F[_]: Monad](db: LookUp[F], startKey: String): F[Option[String]] = {
def loop(currKey: String, seenKeys: Set[String]): F[Option[String]] = {
db.read(currKey).flatMap {
case Some(nextKey) if !seenKeys.contains(nextKey) =>
loop(nextKey, seenKeys + nextKey)
case _ if seenKeys.nonEmpty => seenKeys.mkString("").some.pure[F]
case _ => none[String].pure[F]
}
}
loop(startKey, Set.empty)
}
对于累积值,我已将List
替换为Set
,因为它的contains
方法更有效,但是如果您关心返回结果中的顺序,那么您将不得不返回List
(效率较低)或使用两个累加器(一个Set
,另一个List
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.