5

假设在我的纯 Scala 程序中,我依赖于 Java 服务。此 Java 服务接受一个侦听器,以便在某些数据更改时通知我。

假设数据是一个元组(x,y),只要 X 或 Y 发生变化,java 服务就会调用监听器,但我只对 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)

如果没有 val 或 mutable 集合,我将如何在 Scala 中为这种东西设计一个包装器?我不能在 JavaServiceListener 级别,因为我受方法签名的约束,所以我需要另一个层,Java 侦听器以某种方式转发到该层

4

3 回答 3

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

反应式编程允许您在库处理所有困难的事情时使用纯模型。

于 2018-06-27T13:08:30.307 回答
1

首先,如果您正在设计一个纯函数式程序,则您无法返回Unit(也不能返回Future[Unit],因为Future不会抑制副作用)。

如果性能不是问题,我会使用Kleisli[Option, xType, IO[Unit]]where 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状态。

于 2018-06-27T13:04:15.953 回答
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 解决方案相比,这是否值得值得商榷

于 2018-10-30T07:49:19.427 回答