Scala 转换链如果出错则停止

标签 scala cat scalaz

我想对字符串应用一系列转换,但在出现错误时停止。这是更通用模式的示例(一种责任链模式或访问者模式)

如果可能的话,我现在想避免使用 Cats 或 Scalaz。如果您知道如何在普通 Scala 以及 Cats/Scalaz 上执行此操作,我将很高兴在答案中看到代码;)

因此,以下是我的方法(代码末尾的断言),但是发现错误时它不会停止。基本上是跳过 X 次转换的执行。

type Error = String

sealed trait Transformer {
  def transform(txt:String) : Either[Error, String]
}

object Transformer1 extends Transformer {
  override def transform(txt: String): Either[Error, String] = Right(s"${txt}_One")
}

object Transformer2 extends Transformer {
  override def transform(txt: String): Either[Error, String] = Right(s"${txt}_Two")
}

object Transformer3 extends Transformer {
  override def transform(txt: String): Either[Error, String] = Right(s"${txt}_Three")
}

object TransformerError extends Transformer {
  override def transform(txt: String): Either[Error, String] = Left("Error!!!!")
}

def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] =
  transformers.foldLeft(Right(txt):Either[Error, String])( (result, t) =>  result match {
    case Right(txt) => t.transform(txt)
    case error => error
  } )


val tOk = Seq(Transformer1, Transformer2, Transformer3)
val tError = Seq(Transformer1, TransformerError, Transformer3)

assert(transform("Whatever", tOk) == Right("Whatever_One_Two_Three"))
assert(transform("Whatever", tError) == Left("Error!!!!"))

有什么建议吗?

谢谢!!

最佳答案

在 Scala 2.12 中,Either 是右偏的,因此 for-yield 就可以解决问题。

for {
  v1 <- Transformer1.transform("Whatever")
  v2 <- Transformer2.transform(v1)
  v3 <- Transformer3.transform(v2)
} yield {
  v3
}

计算结果为Right(Whatever_One_Two_Three),而

for {
  v1 <- Transformer1.transform("Whatever")
  v2 <- TransformerError.transform(v1)
  v3 <- Transformer3.transform(v2)
} yield {
  v3
}

计算结果为Left(错误!!!)

但是,如果您想返回应用了所有转换的结果,直到出现错误,即

  assert(transform("Whatever", tError) == Right("Whatever_One"))

那么以下 transform 函数的重构可能会起作用:

  def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] = {

    type Current = Either[Error, String]
    type Previous = Either[Error, String]

    def foldLeftWithEarlyReturn: Tuple2[Current, Previous] = {
      transformers.foldLeft[Tuple2[Current, Previous]](Right(txt) , Right(txt)){
        (result, t)  => result match {
          case ( Right(txt)  , Right(previousTxt)  )   =>        ( t.transform(txt)  , Right(txt)  )
          case ( Left(error) , Right(previousTxt)  )   => return ( Right(previousTxt), Left(error) )
          case e => e
        }
      }
    }

    if (foldLeftWithEarlyReturn._1.isLeft)
      foldLeftWithEarlyReturn._2 // this means last transformation in sequence resulted in Left, so return previous
    else
      foldLeftWithEarlyReturn._1

  }

关于Scala 转换链如果出错则停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50125103/

相关文章:

scala - 如何在Scala Repl和SBT控制台中关闭/打开Typer阶段

scala - 在 sbt 中定义自定义测试配置

bash - 如何取消转义文本文件?

Bash 脚本 : save stream from Serial Port (/dev/ttyUSB0) to file until a specific input (e. g。 eof)出现

scala - 使用scalaz的promise时调用函数

scala - 为什么 Reads 没有声明为协变?

scala - 使用ScalaTest测试Akka Actor

linux - 使用 cat 命令时抑制错误消息

scala - 帮助我理解这个 Scala 代码 : scalaz IO Monad and implicits

eclipse - 可以在 Eclipse 中使用 scalaz 吗?