From c5dfa9c42479f0cc81d8991a2e8530c362d89e85 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Fri, 17 Nov 2023 17:51:52 +0100 Subject: [PATCH] add Var.prism/subType methods without seed and rename *Optic methods --- .../scala/colibri/reactive/Reactive.scala | 28 +++++++++++++---- .../src/test/scala/colibri/ReactiveSpec.scala | 30 ++++++++++++++++--- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/reactive/src/main/scala/colibri/reactive/Reactive.scala b/reactive/src/main/scala/colibri/reactive/Reactive.scala index 74eed9b2..f64a9462 100644 --- a/reactive/src/main/scala/colibri/reactive/Reactive.scala +++ b/reactive/src/main/scala/colibri/reactive/Reactive.scala @@ -295,18 +295,34 @@ trait Var[A] extends VarState[A] with Rx[A] { final def lens[B](read: A => B)(write: (A, B) => A)(implicit owner: NowOwner): Var[B] = transformVar(_.contramap(write(now(), _)))(_.map(read)) - final def prism[A2](f: A2 => A)(g: A => Option[A2])(seed: => A2): Var[A2] = + final def prismSeed[A2](f: A2 => A)(g: A => Option[A2])(seed: => A2): Var[A2] = transformVar(_.contramap(f))(rx => Rx.observableSync(rx.observable.mapFilter(g).prependEval(seed))) - final def subType[A2 <: A: ClassTag](seed: => A2): Var[A2] = prism[A2]((x: A2) => x) { + final def prism[A2](f: A2 => A)(g: A => Option[A2]): Rx[Option[Var[A2]]] = + this.scan(Option.empty[Var[A2]]) { (prevVar, value) => + (prevVar, g(value)) match { + case (variable@Some(_), Some(_)) => variable + case (None, Some(result)) => Some(prismSeed(f)(g)(result)) + case (_, None) => None + } + } + + final def subTypeSeed[A2 <: A: ClassTag](seed: => A2): Var[A2] = prismSeed[A2]((x: A2) => x) { case a: A2 => Some(a) case _ => None }(seed) - final def imapO[B](optic: Iso[A, B]): Var[B] = imap(optic.reverseGet(_))(optic.get(_)) - final def lensO[B](optic: Lens[A, B]): Var[B] = lens(optic.get(_))((base, zoomed) => optic.replace(zoomed)(base)) - final def prismO[B](optic: Prism[A, B])(seed: => B): Var[B] = - prism(optic.reverseGet(_))(optic.getOption(_))(seed) + final def subType[A2 <: A: ClassTag]: Rx[Option[Var[A2]]] = prism[A2]((x: A2) => x) { + case a: A2 => Some(a) + case _ => None + } + + final def imapOptic[B](optic: Iso[A, B]): Var[B] = imap(optic.reverseGet(_))(optic.get(_)) + final def lensOptic[B](optic: Lens[A, B]): Var[B] = lens(optic.get(_))((base, zoomed) => optic.replace(zoomed)(base)) + final def prismSeedOptic[B](optic: Prism[A, B])(seed: => B): Var[B] = + prismSeed(optic.reverseGet(_))(optic.getOption(_))(seed) + final def prismOptic[B](optic: Prism[A, B]): Rx[Option[Var[B]]] = + prism(optic.reverseGet(_))(optic.getOption(_)) } object Var { diff --git a/reactive/src/test/scala/colibri/ReactiveSpec.scala b/reactive/src/test/scala/colibri/ReactiveSpec.scala index f7e8760c..9994ced5 100644 --- a/reactive/src/test/scala/colibri/ReactiveSpec.scala +++ b/reactive/src/test/scala/colibri/ReactiveSpec.scala @@ -858,7 +858,7 @@ class ReactiveSpec extends AsyncFlatSpec with Matchers { case class Employee(name: String, company: Company) val employee = Var(Employee("jules", Company("wules", 7))) - val zipcode = employee.lensO(GenLens[Employee](_.company.zipcode)) + val zipcode = employee.lensOptic(GenLens[Employee](_.company.zipcode)) zipcode.unsafeSubscribe() @@ -882,42 +882,64 @@ class ReactiveSpec extends AsyncFlatSpec with Matchers { val eventVar: Var[Event] = Var[Event](EventA(0)) val eventNotVar: Var[Event] = Var[Event](EventB("")) - val eventAVar = eventVar.prismO(GenPrism[Event, EventA])(null) - val eventAVar2 = eventVar.subType[EventA](null) - val eventNotAVar = eventNotVar.prismO(GenPrism[Event, EventA])(null) + val eventAVar = eventVar.prismSeedOptic(GenPrism[Event, EventA])(null) + val eventAVar2 = eventVar.subTypeSeed[EventA](null) + val eventNotAVar = eventNotVar.prismSeedOptic(GenPrism[Event, EventA])(null) + val eventAVarRx = eventVar.prismOptic(GenPrism[Event, EventA]) + val eventAVarRx2 = eventVar.subType[EventA] eventAVar.unsafeSubscribe() eventAVar2.unsafeSubscribe() eventNotAVar.unsafeSubscribe() + eventAVarRx.unsafeSubscribe() + eventAVarRx2.unsafeSubscribe() eventVar.nowIfSubscribed() shouldBe EventA(0) eventAVar.nowIfSubscribed() shouldBe EventA(0) eventAVar2.nowIfSubscribed() shouldBe EventA(0) eventNotAVar.nowIfSubscribed() shouldBe null + eventAVarRx.nowIfSubscribed().get.now() shouldBe EventA(0) + eventAVarRx2.nowIfSubscribed().get.now() shouldBe EventA(0) + val prevEventAVarRx = eventAVarRx.nowIfSubscribed().get + val prevEventAVarRx2 = eventAVarRx2.nowIfSubscribed().get eventAVar.set(EventA(1)) eventVar.nowIfSubscribed() shouldBe EventA(1) eventAVar.nowIfSubscribed() shouldBe EventA(1) eventAVar2.nowIfSubscribed() shouldBe EventA(1) + eventNotAVar.nowIfSubscribed() shouldBe null + eventAVarRx.nowIfSubscribed().get.now() shouldBe EventA(1) + eventAVarRx.nowIfSubscribed().get shouldBe prevEventAVarRx + eventAVarRx2.nowIfSubscribed().get.now() shouldBe EventA(1) + eventAVarRx2.nowIfSubscribed().get shouldBe prevEventAVarRx2 eventVar.set(EventB("he")) eventVar.nowIfSubscribed() shouldBe EventB("he") eventAVar.nowIfSubscribed() shouldBe EventA(1) eventAVar2.nowIfSubscribed() shouldBe EventA(1) + eventNotAVar.nowIfSubscribed() shouldBe null + eventAVarRx.nowIfSubscribed() shouldBe None + eventAVarRx2.nowIfSubscribed() shouldBe None eventAVar.set(EventA(2)) eventVar.nowIfSubscribed() shouldBe EventA(2) eventAVar.nowIfSubscribed() shouldBe EventA(2) eventAVar2.nowIfSubscribed() shouldBe EventA(2) + eventNotAVar.nowIfSubscribed() shouldBe null + eventAVarRx.nowIfSubscribed().get.now() shouldBe EventA(2) + eventAVarRx2.nowIfSubscribed().get.now() shouldBe EventA(2) eventVar.set(EventA(3)) eventVar.nowIfSubscribed() shouldBe EventA(3) eventAVar.nowIfSubscribed() shouldBe EventA(3) eventAVar2.nowIfSubscribed() shouldBe EventA(3) + eventNotAVar.nowIfSubscribed() shouldBe null + eventAVarRx.nowIfSubscribed().get.now() shouldBe EventA(3) + eventAVarRx2.nowIfSubscribed().get.now() shouldBe EventA(3) } it should "map and now()" in {