有状态功能管道

时间:2016-05-28 02:27:47

标签: scala functional-programming closures pipeline purely-functional

代码解释了自己。

val s = Seq(1,1,1)
val res: Seq[Int] = s.map(...)
                     .check(count how many 1s, if > 2 throw Exception)
                     .map(...)

我正在寻找这个check函数的简单解决方案。

  • 我可以使用mapclosure来计算和投掷,但我想要纯粹的功能。
  • 我可以使用filtersizereduce,但它会返回一个值,而不会使用以下地图进行恢复。

如何对管道进行有状态检查管道?

3 个答案:

答案 0 :(得分:5)

抛出异常可能并不纯粹。如果您使用的是monadic形式的错误处理,那么您可以执行以下操作:

Option(s.map(foo)).
  filter(m => m.count(_ == 1) < 2).
  map{ s =>
    s.map(bar)
     .filter(baz)
     ...
  }

实际上,如果你想在管道中编写它,并且你不想在match中添加额外的括号,你可以使用通常丰富的{{1}方法:

tap

现在你可以

implicit class TapAnything[A](private val a: A) extends AnyVal {
  def tap[U](f: A => U): A = { f(a); a }
}

(注意:s.map(...) .tap(self => if (self.count(_ == 1) > 1) throw new Exception) .map(...) ... + private val的东西只是向编译器表明它应该试图避免创建一个额外的对象来进行调用。)

答案 1 :(得分:4)

一种解决方案是模式匹配,因此检查将变为:

extends AnyVal

最好返回Option类型(而不是抛出):

> Seq(1, 1) match {
    case ls if (ls.count(_ == 1) <= 2) => ls
    case _ => throw new Exception("oh no!")
  }
List(1, 1): Seq[Int]


> Seq(1, 1, 1) match {
    case ls if (ls.count(_ == 1) <= 2) => ls
    case _ => throw new Exception("oh no!")
  }
java.lang.Exception: oh no!
  $.<init>(Main.scala:177)
  $.<clinit>(Main.scala:-1)

答案 2 :(得分:1)

如果您想传递错误消息而不抛出异常,那么一个好的候选者将是Either[A, B]。这需要在管道中进行更多工作(使用leftright操作数),但允许您传递更长的描述性错误消息,例如Option[T]可以'传达:

val x = Seq(1,1,0)
        .map(_ * 3)
        .map(i => if (i > 2) Right(i) else Left("Was smaller than 2"))
        .map(_.right.map(_ * 3))

x.foreach {
  case Right(i) => println("Yay, right")
  case Left(error) => println(error)
}
scala> :paste
// Entering paste mode (ctrl-D to finish)

  val x = Seq(1,1,0)
    .map(_ * 3)
    .map(i => if (i > 2) Right(i) else Left("Was smaller than 2"))
    .map(_.right.map(_ * 3))

  x.foreach {
    case Right(i) => println("Yay, right")
    case Left(error) => println(error)
  }

// Exiting paste mode, now interpreting.

Yay, right
Yay, right
Was smaller than 2

这可能会稍微不方便,因为Either是无偏见的,但它确实允许您传播行为。