liveBook · Manning

Modify

As explained in Functional Programming in Scala, Second Edition

modify is an atomic Ref operation that computes an updated state and returns an additional value from the previous state.

modify performs one atomic state transition while returning a separate result. Given a function of type A => (A, B), it computes the replacement state A and exposes B to the surrounding effect. section 15.3.3

For a Ref, the transition reads the current value, applies the function, and attempts to install the new value. If another thread changes the value first, the operation retries from the newly observed state. This keeps the state update and returned result aligned. section 15.3.3

Building a scope state machine

A scope stores Open or Closed state in a Ref. Opening an open scope appends a new child and returns an effect producing that child: state.modify { case Open(finalizer, subs) => Open(finalizer, subs :+ child) -> F.unit(child); case Closed() => Closed() -> next }. The resulting F[F[Scope[F]]] is flattened because modify returns an effect as its additional value. Closing uses the same pattern to mark the scope Closed while returning the effect that runs finalizers. section 15.3.3

final class Ref[F[_], A] private (
  underlying: AtomicReference[A],
  delay: [A] => (() => A) => F[A]       #1
):
 
  def get: F[A] = delay(() => underlying.get)
 
  def set(a: A): F[Unit] = delay(() => underlying.set(a))
 
  def modify[B](f: A => (A, B)): F[B] = delay: () =>
    @annotation.tailrec
    def loop(): B =
      val oldA = underlying.get
      val (newA, result) = f(oldA)
      if underlying.compareAndSet(oldA, newA) then result else loop()
    loop()
 
object Ref:
  def apply[F[_], A](initial: A)(using F: Monad[F]): Ref[F, A] =
    new Ref(new AtomicReference[A](initial),
      [A] => (th: () => A) => F.unit(()).map(_ => th()))

Inspect the complete Ref implementation, including get, set, the compare-and-set retry loop, and construction from a Monad. section 15.3.3

The same functional shape appears in State: get the current state, set f(s), and yield Unit. Ref.modify adds atomicity for shared effectful state; State.modify expresses a pure state action. section 6.6 section 15.3.3

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Explains the signature, compare-and-set retry loop, nested effects, and scope-state applications; stops short of broader concurrency theory.
Examples
Uses scope opening and closing, Open and Closed states, registered subscopes, finalizers, and a get-then-set State implementation.
What it assumes
Assumes familiarity with effect types, functions, State actions, and monadic flattening; develops atomic Ref access from its implementation.
What you can do after
Apply it via Ref to implement atomic state-machine transitions that return effects or other values from the update.
How it connects
Connects Ref.modify with State actions, state machines, effect types, resource safety, and compare-and-set.

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.