代码之家  ›  专栏  ›  技术社区  ›  AdrianS

带状态的函数侦听器

  •  5
  • AdrianS  · 技术社区  · 8 年前

    假设在我的纯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侦听器以某种方式转发到

    3 回复  |  直到 8 年前
        1
  •  4
  •   Karl Bielefeldt    8 年前

    我宁愿把它包在 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
    

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

        2
  •  1
  •   St.Antario    8 年前

    首先,如果你正在设计一个纯功能的程序,你不能返回 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 作为一个国家。

        3
  •  0
  •   AdrianS    7 年前

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

    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解决方案而言,这是否值得值得一提,值得商榷。