Skip to content

FAQ ​

Is PureLogic thread-safe? ​

State and Writer use mutable variables internally, scoped to the Logic.run (or State.apply / Writer.apply) call. They are not designed to be shared across threads. This is fine because PureLogic is meant for pure domain logic, which is typically single-threaded. If you need concurrency, use an effect system like ZIO or Cats Effect at the boundary and call into PureLogic from there.

How does Abort work under the hood? ​

Abort uses Scala 3's boundary/break mechanism, which compiles down to a local throw/catch. This means aborting is very fast — there is no Either wrapping at each step like in monadic approaches. The Either is only constructed once, at the Abort.apply boundary.

Can I use multiple State or Reader with different types? ​

Yes. You can have as many capabilities as you want, as long as their type parameters differ:

scala
def program(using State[Account], State[Cart], Reader[Config]): Unit = {
  val balance = get[Account](_.balance)
  val items   = get[Cart](_.items)
  // ...
}

If you have two capabilities with the same type (e.g. two State[Int]), you need to name them to disambiguate (see Capabilities).

What happens to Writer logs when Abort fails? ​

It depends on the nesting order. With the default Logic.run order, Writer is outside Abort, so logs accumulated before the failure are preserved in the result. Logs accumulated in a recover block are rolled back by default (use recoverKeepLog to keep them).

Is State really pure? ​

Yes. While State uses mutable variables internally, this mutation is completely scoped within the State.apply or Logic.run call. From the outside, the function is pure: same inputs always produce same outputs, with no observable side effects. This is the same approach used by Haskell's ST monad.