简体   繁体   English

与猫一起执行递归 monad

[英]Recursive monad execution with cats

There is simple abstract look up service with possibility to retrieve value by key:有一个简单的抽象查找服务,可以通过键检索值:

trait LookUp[F[_]] {
  def read(key: String): F[Option[String]]
}

There is use case of this service, idea is to give implemented storage and accumulator with starting key, then ask for value from db if the result is None then stop and Return None, if the value is found then add it to accumulator list and look up for next value as key from previous call result.这个服务有一个用例,想法是给已实现的存储和累加器提供起始键,然后从数据库中请求值,如果结果为无,则停止并返回无,如果找到该值,则将其添加到累加器列表中并查看将下一个值作为上一个调用结果的键。 Execution stops when retrieved value already is found before or None is retrieved.当检索到的值之前已经找到或没有检索到时,执行停止。 Then a string of all acc elements is returned as result.然后返回所有 acc 元素的字符串作为结果。

Tried like this:像这样试过:

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("")      
  }
}

But I'm not able to get types right getting mismatch errors like:但是我无法正确获取类型,出现不匹配错误,例如:

found   : F[cats.data.OptionT[[_]F[_],String]]
required: cats.data.OptionT[F,String]

Approach number 2:方法二:

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(""))))
    }
  }
}

Getting compiler error:获取编译器错误:

(Found)  F[Option[String]]
required: Option[String]
case Some(value) if(!accum.contains(value)) => m.pure(Left((db.read(value), value :: accum)))

Looks like db.read(value) somehow should be unwrapped out of F看起来 db.read(value) 应该以某种方式从 F 中解开

This looks like a great use case for fs2 :这看起来是fs2 的一个很好的用例:

You should be able to do something like this:你应该能够做这样的事情:

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

You are match -ing on the wrong expression in your first implementation.您在第一个实现中match了错误的表达式。 You should match on result , not on the entire for-comprehension.您应该match result ,而不是整个 for-comprehension 。 The implementation below should do what you're after.下面的实现应该做你所追求的。

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)
}

I've replaced List with Set for the accumulated values because its contains method is more efficient, but if you care about the order in the returned result then you'll have to either go back to List (less efficient) or use two accumulators (one Set , the other List ).对于累积值,我已将List替换为Set ,因为它的contains方法更有效,但是如果您关心返回结果中的顺序,那么您将不得不返回List (效率较低)或使用两个累加器(一个Set ,另一个List )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM