如何在 Scala 中实现惰性链模式
How to implement Lazy Chain Pattern in Scala
我正在尝试定义一组 "LazyChains" 用于在将来处理传入消息。我希望 LazyChains 的 API 与 Scala 集合 APIs(即:Seq、Stream 等)没有区别。这将允许我在消息到达之前以声明方式提前定义 filtering/transformations/actions 。这可能是一个众所周知的模式,但我不知道它的名字,所以这让我很难找到任何结果。
这是我要完成的示例:
val chainA = LazyChain()
.filter(_ > 1)
.map(x => x * 2)
.foreach(println _)
val chainB = LazyChain()
.filter(_ > 5)
.flatMap(x => Seq(x, x))
.foreach(println _)
chainA.apply(2) // should print "4"
chainA.apply(1) // should print nothing
chainB.apply(10) // should print "10" - twice
此模式是否已存在于 Scala 集合中 API?如果没有,我该如何实现这个 class LazyChain
?
这是我目前的尝试。我似乎不知道如何让类型工作:
case class LazyChain[I, O](val filter : Option[I => Boolean],
val transform : I => O,
val action : Option[O => Unit]) {
def filter(otherFilter : I => Boolean): LazyChain[I, O] = {
val newFilter = Some({ x : I => {
filter.map(_.apply(x)).getOrElse(true) && otherFilter.apply(x)
}})
copy(filter = newFilter)
}
def map[X](otherTransform : O => X) : LazyChain[I, X] = {
new LazyChain[I, X](
filter = filter,
transform = (x: I) => {
otherTransform.apply(transform.apply(x))
},
/*
type mismatch;
[error] found : Option[O => Unit]
[error] required: Option[X => Unit]
*/
action = action
)
}
def flatMap[X](otherTransform : O => Seq[X]) : LazyChain[I, X] = {
new LazyChain[I, X](
filter = filter,
transform = (x: I) => {
/**
type mismatch;
[error] found : Seq[X]
[error] required: X
*/
otherTransform.apply(transform.apply(x))
}
)
}
def foreach(newAction : O => Unit) = {
copy(action = Some(newAction))
}
def apply(element : I) = {
if (filter.map(_.apply(element)).getOrElse(true)) {
val postTransform = transform.apply(element)
action.foreach(_.apply(postTransform))
}
}
}
object LazyChain {
def apply[X]() : LazyChain[X, X] = {
new LazyChain(filter = None, transform = x => x, action = None)
}
}
您想要的只是用一些奇特的方法包装一个函数 I => List[O]
。您可以编写 implicit class
将这些方法添加到此类型,但 Kleisli 通过各种猫类型 类 免费完成大部分操作,主要是 FilterFunctor
.
import cats.implicits._
import cats.data.Kleisli
type LazyChain[I, O] = Kleisli[List, I, O]
def lazyChain[A]: LazyChain[A, A] = Kleisli[List, A, A](a => List(a))
val chainA = lazyChain[Int]
.filter(_ > 1)
.map(x => x * 2)
.map(println)
val chainB = lazyChain[Int]
.filter(_ > 5)
.flatMapF(x => List(x, x))
.map(println)
chainA(2) // should print "4"
chainA(1) // should print nothing
chainB(10) // should print "10" - twice
可能看起来有点太神奇了,所以这是一个手工版本:
case class LazyChain[A, B](run: A => List[B]) {
def filter(f: B => Boolean): LazyChain[A, B] = chain(_.filter(f))
def map[C](f: B => C): LazyChain[A, C] = chain(_.map(f))
def flatMap[C](f: B => List[C]): LazyChain[A, C] = chain(_.flatMap(f))
def chain[C](f: List[B] => List[C]): LazyChain[A, C] = LazyChain(run andThen f)
}
object LazyChain {
def apply[I]: LazyChain[I, I] = new LazyChain(a => List(a))
}
链接转换是一个普遍关注的问题,正如评论所说,使用 monix.Observable、iteratees 等是解决此问题的正确方法(而不是普通的 List
和流天生懒惰
我正在尝试定义一组 "LazyChains" 用于在将来处理传入消息。我希望 LazyChains 的 API 与 Scala 集合 APIs(即:Seq、Stream 等)没有区别。这将允许我在消息到达之前以声明方式提前定义 filtering/transformations/actions 。这可能是一个众所周知的模式,但我不知道它的名字,所以这让我很难找到任何结果。
这是我要完成的示例:
val chainA = LazyChain()
.filter(_ > 1)
.map(x => x * 2)
.foreach(println _)
val chainB = LazyChain()
.filter(_ > 5)
.flatMap(x => Seq(x, x))
.foreach(println _)
chainA.apply(2) // should print "4"
chainA.apply(1) // should print nothing
chainB.apply(10) // should print "10" - twice
此模式是否已存在于 Scala 集合中 API?如果没有,我该如何实现这个 class LazyChain
?
这是我目前的尝试。我似乎不知道如何让类型工作:
case class LazyChain[I, O](val filter : Option[I => Boolean],
val transform : I => O,
val action : Option[O => Unit]) {
def filter(otherFilter : I => Boolean): LazyChain[I, O] = {
val newFilter = Some({ x : I => {
filter.map(_.apply(x)).getOrElse(true) && otherFilter.apply(x)
}})
copy(filter = newFilter)
}
def map[X](otherTransform : O => X) : LazyChain[I, X] = {
new LazyChain[I, X](
filter = filter,
transform = (x: I) => {
otherTransform.apply(transform.apply(x))
},
/*
type mismatch;
[error] found : Option[O => Unit]
[error] required: Option[X => Unit]
*/
action = action
)
}
def flatMap[X](otherTransform : O => Seq[X]) : LazyChain[I, X] = {
new LazyChain[I, X](
filter = filter,
transform = (x: I) => {
/**
type mismatch;
[error] found : Seq[X]
[error] required: X
*/
otherTransform.apply(transform.apply(x))
}
)
}
def foreach(newAction : O => Unit) = {
copy(action = Some(newAction))
}
def apply(element : I) = {
if (filter.map(_.apply(element)).getOrElse(true)) {
val postTransform = transform.apply(element)
action.foreach(_.apply(postTransform))
}
}
}
object LazyChain {
def apply[X]() : LazyChain[X, X] = {
new LazyChain(filter = None, transform = x => x, action = None)
}
}
您想要的只是用一些奇特的方法包装一个函数 I => List[O]
。您可以编写 implicit class
将这些方法添加到此类型,但 Kleisli 通过各种猫类型 类 免费完成大部分操作,主要是 FilterFunctor
.
import cats.implicits._
import cats.data.Kleisli
type LazyChain[I, O] = Kleisli[List, I, O]
def lazyChain[A]: LazyChain[A, A] = Kleisli[List, A, A](a => List(a))
val chainA = lazyChain[Int]
.filter(_ > 1)
.map(x => x * 2)
.map(println)
val chainB = lazyChain[Int]
.filter(_ > 5)
.flatMapF(x => List(x, x))
.map(println)
chainA(2) // should print "4"
chainA(1) // should print nothing
chainB(10) // should print "10" - twice
可能看起来有点太神奇了,所以这是一个手工版本:
case class LazyChain[A, B](run: A => List[B]) {
def filter(f: B => Boolean): LazyChain[A, B] = chain(_.filter(f))
def map[C](f: B => C): LazyChain[A, C] = chain(_.map(f))
def flatMap[C](f: B => List[C]): LazyChain[A, C] = chain(_.flatMap(f))
def chain[C](f: List[B] => List[C]): LazyChain[A, C] = LazyChain(run andThen f)
}
object LazyChain {
def apply[I]: LazyChain[I, I] = new LazyChain(a => List(a))
}
链接转换是一个普遍关注的问题,正如评论所说,使用 monix.Observable、iteratees 等是解决此问题的正确方法(而不是普通的 List
和流天生懒惰