1

cats中,当Monad使用 trait 创建a 时,理想情况下应提供Monad方法的尾递归实现以确保堆栈安全。tailRecM

我正在使用无标记的最终方法,并希望对我的程序产生Task[Validated[String, ?]](Monix ) 的效果。Task

我不知道如何编写尾递归实现。我的非尾递归解决方案是:

import cats.Monad
import cats.data.Validated
import cats.data.Validated.{Invalid, Valid}
import monix.eval.Task

final case class TaskValidated[A](value: Task[Validated[String, A]])

implicit val taskValidatedMonad: Monad[TaskValidated] = 
    new Monad[TaskValidated] {

        override def flatMap[A, B](fa: TaskValidated[A])(f: A => TaskValidated[B]): TaskValidated[B] =  
            new TaskValidated[B](   
                fa.value.flatMap {  
                    case Valid(a)   => f(a).value   
                    case Invalid(s) => Task(Invalid(s)) 
                }   
            )

        override def pure[A](a: A): TaskValidated[A] = TaskValidated(Task(Valid(a)))

        // @annotation.tailrec  
        def tailRecM[A, B](init: A)(fn: A => TaskValidated[Either[A, B]]): TaskValidated[B] = { 
            TaskValidated(fn(init).value.flatMap {  
                case Invalid(s)      => Task.now(Invalid(s))    
                case Valid(Right(b)) => Task.now(Valid(b))  
                case Valid(Left(a))  => tailRecM(a)(fn).value   
            })  
        }   
    }
4

1 回答 1

1

Task有它自己的tailRecM,所以使用它是有意义的。尝试

def tailRecM[A, B](init: A)(fn: A => TaskValidated[Either[A, B]]): TaskValidated[B] = {
  def aux(fn: A => Task[Validated[String, Either[A, B]]]): Task[Validated[String, B]] = {
    def fn1(a: A): Task[Either[A, B]] = fn(a).flatMap {
      case Valid(either) => Task.now(either)
      case Invalid(s)    => Task.raiseError(new RuntimeException(s))
    }

    Task.tailRecM(init)(fn1).map(Valid(_))
  }

  TaskValidated(aux(fn(_).value))
}
于 2018-10-07T21:19:52.487 回答