Let's say in my pure Scala program i have a dependency to a Java service. This Java service accepts a listener to notify me when some data changes.
Let's say the data is a tuple(x, y) and the java service calls the listener whenever X or Y changes but i'm interested only when X.
For this my listener has to save the last value of X, and forward the update/call only when oldX != X, so in order to have this my impure scala listener implementation has to hold a 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)
How would i go about to design a wrapper for this kind of thing in Scala without val or mutable collections ? I can't at the JavaServiceListener level since i'm bound by the method signature, so I need another layer above which the java listener forwards to somehow
I found the solution I like with 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()
More stuff about handling shared state here https://github.com/systemfw/scala-italy-2018
it is debatable if this is worth it over the private var solution