Skip to content

Commit

Permalink
Override fix in Defer instance, to avoid lazy val
Browse files Browse the repository at this point in the history
  • Loading branch information
durban committed May 24, 2024
1 parent 6c4ee2d commit 88bbc77
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 1 deletion.
9 changes: 9 additions & 0 deletions core/shared/src/main/scala/dev/tauri/choam/core/Rxn.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,15 @@ private sealed abstract class RxnInstances6 extends RxnInstances7 { self: Rxn.ty
implicit final def deferInstance[X]: Defer[Rxn[X, *]] = new Defer[Rxn[X, *]] {
final override def defer[A](fa: => Rxn[X, A]): Rxn[X, A] =
self.computed[X, A] { x => fa.provide(x) }
final override def fix[A](fn: Rxn[X, A] => Rxn[X, A]): Rxn[X, A] = {
// This is in effect a "thread-unsafe lazy val";
// we know exactly how `defer` works, so it's safe
// to do this here (and this way we avoid the
// synchronization of an actual lazy val):
val ref = new scala.runtime.ObjectRef[Rxn[X, A]](null)
ref.elem = fn(defer { ref.elem })
ref.elem
}
}
}

Expand Down
23 changes: 22 additions & 1 deletion core/shared/src/test/scala/dev/tauri/choam/RxnSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicInteger

import scala.concurrent.duration._

import cats.{ Applicative, Monad, StackSafeMonad, Align }
import cats.{ Applicative, Monad, StackSafeMonad, Align, Defer }
import cats.arrow.{ ArrowChoice, FunctionK }
import cats.data.Ior
import cats.effect.IO
Expand Down Expand Up @@ -1007,6 +1007,27 @@ trait RxnSpec[F[_]] extends BaseSpecAsyncF[F] { this: McasImplSpec =>
} yield ()
}

test("Defer instance") {
val inst = Defer[Rxn[Int, *]]
for {
ctr <- F.delay { new AtomicInteger }
rxn0 = inst.defer {
ctr.getAndIncrement()
Rxn.pure("result")
}
_ <- assertEqualsF(ctr.get(), 0)
_ <- assertResultF(rxn0[F](99), "result")
ref <- Ref.unpadded("-").run[F]
rxn1 = inst.fix[Int] { rec =>
ref.unsafeCas("a", "b").as(42) + (Axn.unsafe.delay {
if (!ref.loc.unsafeCasV("-", "a")) { throw new AssertionError }
} *> Rxn.unsafe.retry) + rec
}
_ <- assertResultF(rxn1[F](99), 42)
_ <- assertResultF(ref.get.run[F], "b")
} yield ()
}

test("Ref.Make instance") {
val inst = implicitly[CatsRef.Make[Rxn[Int, *]]]
val rxn = inst.refOf(42).flatMap { ref =>
Expand Down

0 comments on commit 88bbc77

Please sign in to comment.