有状态函数流水线
Stateful function pipeline
代码自行解释。
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
,但它 return 是一个值,无法通过以下地图恢复。
如何制作 pure 和 stateful 检查管道到管道?
抛出异常可以说是不纯粹的。如果您改为使用单子形式的错误处理,您将执行如下操作:
Option(s.map(foo)).
filter(m => m.count(_ == 1) < 2).
map{ s =>
s.map(bar)
.filter(baz)
...
}
实际上,如果您想在管道内编写它,并且不想添加 match
所必需的额外括号,您可以使用 commonly-enriched 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
+ extends AnyVal
只是向编译器表明它应该尽量避免创建额外的对象来进行调用)。
一种解决方案是模式匹配,因此检查将变为:
> 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)
可能比 return 选项类型(而不是抛出)更可取:
> Seq(1, 1, 1) match {
case ss if (ss.count(_ == 1) <= 2) => Option(ss)
case _ => None
}
None: Option[Seq[Int]]
如果您想在不引发异常的情况下传递错误消息,Either[A, B]
是一个不错的选择。这将需要更多的工作(使用 left
或 right
操作数)在管道中,但允许您传递一个长的更具描述性的错误消息,例如, Option[T]
可以' 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
是公正的,但它确实允许您流动行为。
代码自行解释。
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
,但它 return 是一个值,无法通过以下地图恢复。
如何制作 pure 和 stateful 检查管道到管道?
抛出异常可以说是不纯粹的。如果您改为使用单子形式的错误处理,您将执行如下操作:
Option(s.map(foo)).
filter(m => m.count(_ == 1) < 2).
map{ s =>
s.map(bar)
.filter(baz)
...
}
实际上,如果您想在管道内编写它,并且不想添加 match
所必需的额外括号,您可以使用 commonly-enriched 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
+ extends AnyVal
只是向编译器表明它应该尽量避免创建额外的对象来进行调用)。
一种解决方案是模式匹配,因此检查将变为:
> 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)
可能比 return 选项类型(而不是抛出)更可取:
> Seq(1, 1, 1) match {
case ss if (ss.count(_ == 1) <= 2) => Option(ss)
case _ => None
}
None: Option[Seq[Int]]
如果您想在不引发异常的情况下传递错误消息,Either[A, B]
是一个不错的选择。这将需要更多的工作(使用 left
或 right
操作数)在管道中,但允许您传递一个长的更具描述性的错误消息,例如, Option[T]
可以' 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
是公正的,但它确实允许您流动行为。