比方说,在我的纯Scala程序中,我依赖Java服务。 此Java服务接受侦听器在某些数据更改时通知我。
假设数据是一个元组(x,y),并且Java服务会在X或Y发生更改时调用侦听器,但我仅在X时才感兴趣。
为此,我的侦听器必须保存X的最后一个值,并且仅在oldX!= X时才转发更新/调用,因此,为了使此不纯的Scala侦听器实现必须拥有一个var oldX
val listener = new JavaServiceListener() {
var oldX;
def updated(val x, val y): Unit = {
if (oldX != x) {
oldX = x
//do stuff
}
}
javaService.register(listener)
我将如何在Scala中为这种没有Val或可变集合的东西设计包装器?由于受方法签名的约束,我无法进入JavaServiceListener级别,因此我需要Java侦听器以某种方式转发到其上的另一层
答案 0 :(得分:4)
我的偏好是将其包装在Monix Observable中,然后可以使用distinctUntilChanged
消除连续的重复项。像这样:
import monix.reactive._
val observable = Observable.create(OverflowStrategy.Fail(10)){(sync) =>
val listener = new JavaServiceListener() {
def updated(val x, val y): Unit = {
sync.onNext(x)
}
}
javaService.register(listener)
Cancelable{() => javaService.unregister(listener)}
}
val distinctObservable = observable.distinctUntilChanged
反应式编程使您可以在库处理所有困难内容时使用纯模型。
答案 1 :(得分:1)
首先,如果您设计的是纯函数式程序,则无法返回Unit
(都不返回Future[Unit]
,因为Future
不会抑制副作用)。
如果性能不是问题,我会使用Kleisli[Option, xType, IO[Unit]]
,其中T = Option
。因此,您要做的第一件事就是定义(添加适当的类型)
def updated(oldX, x): Kleisli[Option, xType, xType] = Kleisli liftF {
if(x != oldX) None
else Some(x)
}
def doStuff(x, y): Kleisli[Option, xType, IO[Unit]] = Kleisli pure {
IO{
//doStuff
}
}
现在您可以像这样理解它们:
val result: Kleisli[Option, xType, IO[Unit]] = for{
xx <- updated(oldX, x)
effect <- doStuff(xx, y)
} yield effect
您可以使用ReaderWriterStateT
执行有状态计算,因此可以将oldX
保留为状态。
答案 2 :(得分:0)
我找到了我喜欢的Cats and Cats-Effect解决方案:
trait MyListener {
def onChange(n: Int): Unit
}
class MyDistinctFunctionalListener(private val last: Ref[IO, Int], consumer: Int => Unit) extends MyListener {
override def onChange(newValue: Int): Unit = {
val program =
last
.getAndSet(newValue)
.flatMap(oldValue => notify(newValue, oldValue))
program.unsafeRunSync()
}
private def notify(newValue: Int, oldValue: Int): IO[Unit] = {
if (oldValue != newValue) IO(consumer(newValue)) else IO.delay(println("found duplicate"))
}
}
object MyDistinctFunctionalListener {
def create(consumer: Int => Unit): IO[MyDistinctFunctionalListener] =
Ref[IO].of(0).map(v => new MyDistinctFunctionalListener(v, consumer))
}
val printer: Int => Unit = println(_)
val functionalDistinctPrinterIO = MyDistinctFunctionalListener.create(printer)
functionalDistinctPrinterIO.map(fl =>
List(1, 1, 2, 2, 3, 3, 3, 4, 5, 5).foreach(fl.onChange)
).unsafeRunSync()
更多关于在https://github.com/systemfw/scala-italy-2018处处理共享状态的信息
在私有var解决方案中是否值得这样做是值得商的