代码解释了自己。
val s = Seq(1,1,1)
val res: Seq[Int] = s.map(...)
.check(count how many 1s, if > 2 throw Exception)
.map(...)
我正在寻找这个check
函数的简单解决方案。
map
和closure
来计算和投掷,但我想要纯粹的功能。filter
和size
或reduce
,但它会返回一个值,而不会使用以下地图进行恢复。如何对管道进行纯和有状态检查管道?
答案 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]
。这需要在管道中进行更多工作(使用left
或right
操作数),但允许您传递更长的描述性错误消息,例如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
是无偏见的,但它确实允许您传播行为。