具有状态的功能监听器

Bli*_*1eg 5 functional-programming scala referential-transparency purely-functional

让我们说在我的纯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)
Run Code Online (Sandbox Code Playgroud)

如何在没有val或可变集合的情况下为Scala设计这种东西的包装器?我不能在JavaServiceListener级别,因为我受到方法签名的约束,所以我需要另一个层,在这个层之上,java监听器以某种方式转发

Bli*_*1eg 0

我找到了我喜欢的猫和猫效应的解决方案:

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()
Run Code Online (Sandbox Code Playgroud)

有关处理共享状态的更多内容请参见https://github.com/systemfw/scala-italy-2018

这是否比私有 var 解决方案值得值得争论