Ленивый набор функций, замкнутых на первый успех - PullRequest
0 голосов
/ 21 сентября 2019

Цель состоит в том, чтобы лениво оценить набор функций, останавливающихся и возвращающих первый счастливый результат.Вычисления должны быть последовательными.Вот моя попытка

def f1(i: Int): Either[String, Int] = {println(s"f1($i)"); Left("boom-f1") }
def f2(i: Int): Either[String, Int] = {println(s"f2($i)"); Left("boom-f2") }
def f3(i: Int): Either[String, Int] = {println(s"f3($i)"); Right(i) }

val in = 42

(f1(in) #:: f2(in) #:: f3(in) #:: Stream.empty) collectFirst { case Right(x) => x } toRight("boom")

, которая выдает

f1(42)
f2(42)
f3(42)
res0: Either[String,Int] = Right(42)

, где мы видим все три выполненных, тогда как

def f1(i: Int): Either[String, Int] = {println(s"f1($i)"); Right(i) }
def f2(i: Int): Either[String, Int] = {println(s"f2($i)"); Right(i) }
def f3(i: Int): Either[String, Int] = {println(s"f3($i)"); Right(i) }

выдаст

f1(42)
res0: Either[String,Int] = Right(42)

где мы видим только одного казненного.

Предоставляют ли кошки абстракцию для такого ленивого обхода, связанного с неудачами?

Ответы [ 2 ]

1 голос
/ 21 сентября 2019

Ах, я был слишком сложен, orElse уже реализует такую ​​семантику и выдает просто

f1(in) orElse f2(in) orElse f3(in) orElse Left("boom")

, поскольку он принимает параметр по имени.

1 голос
/ 21 сентября 2019

Вы можете использовать тип данных Ior , который является инклюзивным или взаимосвязанным между двумя типами данных.

import cats.data.Ior
import cats.implicits._

import scala.annotation.tailrec

object Main2 {
  def main(args: Array[String]) : Unit = {
    def f1(i: Int): Either[String, Int] = {println(s"f1($i)"); Left("boom-f1") }
    def f2(i: Int): Either[String, Int] = {println(s"f2($i)"); Left("boom-f2") }
    def f3(i: Int): Either[String, Int] = {println(s"f3($i)"); Right(i) }

    def traverseLazy(input: Int, list: List[Int => Either[String, Int]]): Ior[List[String], Int] = {
      @tailrec
      def go(ls: List[Int => Either[String, Int]], acc: List[String]): Ior[List[String], Int] = ls match {
        case x :: xs => x(input) match  {
          case Left(error) => go(xs, error :: acc)
          case Right(value) => if (ls.isEmpty) value.rightIor else Ior.both(acc, value)
        }
        case Nil => acc.leftIor
      }

      go(list, List.empty)
    }

    val res = traverseLazy(42, List(f1, f2, f3)).fold(
      _.intercalate("\n"),
      res => s"succeeded with $res",
      (errors, res) => s"completed successfully with res $res but some errors were also found: ${errors.intercalate(", ")}")

    println(res)
  }
}
...