Introduction to the Compose Snapshot system
Tags: android, kotlin, compose
Published: 2021-05-29 02:35:05

{% cta https://blog.zachklipp.com/introduction-to-the-compose-snapshot-system/ %} This post has moved to blog.zachklipp.com. {% endcta %}

---

Jetpack Compose introduces a novel way to handle observable state. For an introduction that adds some historical context about reactive programming on Android, see the prequel to this post, [A historical introduction to the Compose reactive state model](https://blog.zachklipp.com/a-historical-introduction-to-the-compose-reactive-state-model/). This post explores the lower level snapshot APIs. A lot of the stuff here has some pretty deep academic/computer science roots, but this isn’t a dissertation so I’m going to come at it from a more pragmatic angle. I might do another post in the future that looks at the implementation of the snapshot machinery under the hood, which is even more complex and probably too much content to reasonably fit into this post anyway.

# State in Compose

In Compose, any state that is read by a composable function should be backed by a special state object returned by functions like these:

- [`mutableStateOf`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#mutableStateOf(kotlin.Any,androidx.compose.runtime.SnapshotMutationPolicy)
)/[`MutableState`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/MutableState)
- [`mutableStateListOf`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#mutableStateListOf()
)/[`SnapshotStateList`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList)
- [`mutableStateMapOf`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#mutableStateMapOf()
)/[`SnapshotStateMap`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap)
- [`derivedStateOf`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#derivedStateOf(kotlin.Function0)
)
- [`rememberUpdatedState`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#rememberUpdatedState(kotlin.Any)
)
- `collect*AsState`

Basically, anything that implements the [`State<T>`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/State) interface (including `MutableState<T>`) or the [`StateObject`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/StateObject) interface (which the built-in implementations of `MutableState` actually do as well, if you look deep enough). If you’re wondering why I didn’t mention `remember`, take a few minutes to go read [this post](https://blog.zachklipp.com/remember-mutablestateof-a-cheat-sheet/) explaining the difference.

The primary reason for using snapshot state objects to hold your state, or at least the first one you’ll probably come across when learning Compose, is so that your composables will automatically update when that state changes. Another reason that is just as important but not discussed as often is so that changes to composable state from different threads (for example, from `LaunchedEffect`s) are appropriately isolated and can be performed in a safe manner, without race conditions. It turns out both these reasons are related.

> **Note on terminology:** I’ll be using the term _“snapshot state”_ or _“state value”_ to refer to things like `MutableState`s. The term “snapshot state” appears in the Compose source and documentation. The reason why will hopefully be apparent by the end of this post!

Keep in mind that most of the APIs introduced below are not intended to be used by everyday code. You don’t need to even know about any of them to use Compose. A lot of these APIs are easy to mess up and get yourself in trouble – for example, a lot of them require explicitly disposing various handles. They are useful for building higher-level concepts on top of, such as the `snapshotFlow` function, or the glue code that the Compose runtime uses to trigger recomposition, but it’s very unlikely you’d ever need to reach for them if you’re building apps. Nevertheless, it is useful to see how these low-level building blocks work so that you can reason about the higher-level ones and are more prepared to go read the Compose source if you ever have the need or inclination to do so.

## The “snapshot”

Compose defines a type called `Snapshot` and a bunch of APIs for working with them. A `Snapshot` is a lot like a save point in a video game: it represents the state of your entire program at a single point in history. Well, that’s not entirely true – it represents the state of all the _snapshot state values_ that existed in your program when it was taken, so for the sake of keeping the explanation simple let’s assume all your state is in snapshot states. Any code can take a snapshot – these are public APIs.

To help illustrate, I’ll introduce the example of a simple `Dog` class and a `main` function that performs some operations on it. `Dog`s support snapshots because their names are stored in `MutableState` objects.

```kotlin
class Dog {
  var name: MutableState<String> = mutableStateOf(“”)
}

fun main() {
  val dog = Dog()
  dog.name.value = “Spot”
  val snapshot = Snapshot.takeSnapshot()
  dog.name.value = “Fido”

  // When finished with the snapshot, it must always be disposed.
  // Subsequent examples omit this step for brevity, but don’t
  // forget to do it if you copy and paste any of this code!
  snapshot.dispose()
}
```

> **Notes on examples**
>
> I’ll use explicit `MutableState` properties in the example code, without property delegation (the `by` syntax), to make the code more clear.
>
> Most of the example content is in `main` functions. If you’re trying to run these yourself, you can copy and paste the code into JUnit test functions for easy running from an IDE. However, the snapshot system, while shipped as part of Compose, is independent from and does not require other Compose features, so theoretically they should be runnable as regular command-line programs.

## “Restoring” snapshots

What good is a save point if you can’t restore it? In a similar way, snapshots can be restored - kind of. You can’t overwrite the current state of the world. Instead, you give the snapshot a function (i.e. a lambda) to run. The snapshot will run the function, and inside the function all the state values will have the value they were snapshotted with.

Let’s try changing our dog’s name and reading the initial one from the snapshot.

```kotlin
fun main() {
  val dog = Dog()
  dog.name.value = “Spot”
  val snapshot = Snapshot.takeSnapshot()
  dog.name.value = “Fido”

  println(dog.name.value)
  snapshot.enter { println(dog.name.value) }
  println(dog.name.value)
}

// Output:
Fido
Spot
Fido
```

If that doesn’t seem impressive, remember that `takeSnapshot()` will take a snapshot of _all_ the state values in your program, _no matter where they were created or set_. The `enter` function will temporarily restore the snapshot states to _all_ code in the function, even if it has a deep call stack:

```kotlin
fun main() {
  // …
  snapshot.enter { reminisce(dog) }
}

private fun reminisce(dog: Dog) {
  printName(dog.name)
}

// MutableState<T> extends the interface State<T>
private fun printName(name: State<String>) {
  println(name.value)
}
```

So far all we’ve done is read snapshotted data though. Real programs change data, so how do we do that?

## Mutable snapshots

Let’s try changing our dog’s name inside that `enter` block:

```kotlin
fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  val snapshot = Snapshot.takeSnapshot()

  println(dog.name.value)
  snapshot.enter {
    println(dog.name.value)
    dog.name.value = "Fido"
    println(dog.name.value)
  }
  println(dog.name.value)
}

// Output:
Spot

java.lang.IllegalStateException: Cannot modify a state object in a read-only snapshot
```
Snapshots created with `takeSnapshot()` are read-only. Inside the `enter` block, we can read back the snapshotted values, but we can’t write to them. We need a different function to create a _mutable_ snapshot: `takeMutableSnapshot()`. This function returns an instance of a `MutableSnapshot`, which is just like a `Snapshot`, but it has some extra powers. It also has an `enter` method, but its `enter` method can modify state values.

Let’s fix our crash:
```kotlin
fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  val snapshot = Snapshot.takeMutableSnapshot()
  println(dog.name.value)
  snapshot.enter {
    dog.name.value = "Fido"
    println(dog.name.value)
  }
  println(dog.name.value)
}

// Output:
Spot
Fido
Spot 
```

No crash! But wait – even after changing the name in the `enter` block, the change is reverted as soon as `enter` returns. Snapshots don’t only allow you to peek into the past, they also allow you to isolate changes and prevent them from affecting anything else. But what if we want to “save” those changes? We need to “apply” the snapshot.

```kotlin
fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  val snapshot = Snapshot.takeMutableSnapshot()
  println(dog.name.value)
  snapshot.enter {
    dog.name.value = "Fido"
    println(dog.name.value)
  }
  println(dog.name.value)
  snapshot.apply()
  println(dog.name.value)
}

// Output:
Spot
Fido
Spot
Fido 
```

Voila! After calling `apply()` the new value is visible outside the `enter` block! The pattern of taking a mutable snapshot, running a function on it, and then applying it is so common there’s a helper function that handles the boilerplate for us: [`Snapshot.withMutableSnapshot()`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/Snapshot.Companion#withMutableSnapshot(kotlin.Function0)
). We can simplify the above example like this:

```kotlin
fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  Snapshot.withMutableSnapshot {
    println(dog.name.value)
    dog.name.value = "Fido"
    println(dog.name.value)
  }
  println(dog.name.value)
}
```

Let’s recap. So far we can:

- take snapshots of all our state,
- “restore” those snapshots for a particular block of code, and
- change state values.

We still haven’t seen how to actually observe changes though. Let’s figure that out next.

## Tracking reads and writes

Observing changes to state using an observer pattern is a two part process. First, the parts of the code that depend on the state register change listeners. Second, when the value changes, all registered listeners are notified. Manually registering observers, and remembering to unregister them, can be error prone. Compose state helps out here by letting you register observers on state read - without any explicit function call! For example, when the code reading the state is a composable function, the Compose runtime updates it by recomposing the function.

The `takeMutableSnapshot()` function actually has a couple optional parameters. It takes a “read observer” and a “write observer” – both are simple `(Any) -> Unit` functions that will be called any time a snapshot state value is read or written, respectively, inside an `enter` block. What you do with these values is really up to you, although since the values could be literally anything, you can’t really do much. One thing you can do is simply to track all the values read from the snapshot in some data structure so that you can register a change listener in the future and determine whether any changed values are ones that you previously read, although Compose includes a helper class to do that for you (see below).  

Let’s see if our dog’s name is accessed:

```kotlin
fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  val readObserver: (Any) -> Unit = { readState ->
    if (readState == dog.name) println("dog name was read")
  }
  val writeObserver: (Any) -> Unit = { writtenState ->
    if (writtenState == dog.name) println("dog name was written")
  }

  val snapshot = Snapshot.takeMutableSnapshot(readObserver, writeObserver)
  println("name before snapshot: " + dog.name.value)
  snapshot.enter {
    dog.name.value = "Fido"
    println("name before applying: ")
    // This could be inlined, but I've separated the actual state read
    // from the print statement to make the output sequence more clear.
    val name = dog.name.value
    println(name)
  }
  snapshot.apply()
  println("name after applying: " + dog.name.value)

}

// Output:
name before snapshot: Spot
dog name was written
name before applying: 
dog name was read
Fido
name after applying: Fido
```

We can see that the `MutableState` instance stored in the `dog.name` property gets passed to our read and write observers. The observers are invoked immediately when the value is accessed too – note in the above output that “dog name was read” is printed before the actual name.

Another advantage of snapshots is that state reads are observed no matter how deep in the call stack they occur. This means you can factor code that reads state out into functions or properties, and those reads will still be tracked. The property delegate extensions on the `State` types rely on this. To prove it, we can add some indirection to our example and see that the reads are still reported:

```kotlin
fun Dog.getActualName() = nameValue
val Dog.nameValue get() = name.value

fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  val readObserver: (Any) -> Unit = { readState ->
    if (readState == dog.name) println("dog name was read")
  }

  val snapshot = Snapshot.takeSnapshot(readObserver)
  snapshot.enter {
    println("reading dog name")
    val name = dog.getActualName()
    println(name)
  }
}

// Output:
reading dog name
dog name was read
Spot
```

State changes are de-duplicated. If a state value is set to the same value, no change is recorded for that value. The logic that defines what values are considered “equal” can actually be customized via a policy, as we’ll see later in this post when we examine conflicting snapshot writes.

### SnapshotStateObserver

The pattern of tracking all the reads in a particular function and then executing a callback when any of those state values is changed is so common that there’s a class that implements it for us: [`SnapshotStateObserver`](developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver). The usage of the class looks something like this:

1. Create a `SnapshotStateObserver` and pass in a function that acts as a Java-style `Executor` to run change notification callbacks.
2. Call `start()` to start watching for changes.
3. Call `observeReads()` one or more times, passing it a function to observe reads from as well as a callback to execute when any of the read values is changed. Every time the callback is invoked, the set of states being observed is cleared and `observeReads()` must be called again in order to continue tracking changes.
4. Call `stop()` and `clear()` to stop watching for changes and release resources required to do so.

This is a bit of a longer example, but shows how we can observe state changes to our dog’s name using `SnapshotStateObserver`:

```kotlin
fun main() {
  val dog = Dog()

  fun immediateExecutor(runnable: () -> Unit) {
    runnable()
  }

  fun blockToObserve() {
    println("dog name: ${dog.name.value}")
  }

  val observer = SnapshotStateObserver(::immediateExecutor)

  fun onChanged(scope: Int) {
    println("something was changed from pass $scope")
    println("performing next read pass")
    observer.observeReads(
      scope = scope + 1,
      onValueChangedForScope = ::onChanged,
      block = ::blockToObserve
    )
  }

  dog.name.value = "Spot"

  println("performing initial read pass")
  observer.observeReads(
    // This can be literally any object, it doesn't need
    // to be an int. This example just uses an int to
    // demonstrate subsequent read passes.
    scope = 0,
    onValueChangedForScope = ::onChanged,
    block = ::blockToObserve
  )

  println("starting observation")
  observer.start()

  println("initial state change")
  Snapshot.withMutableSnapshot {
    dog.name.value = "Fido"
  }

  println("second state change")
  Snapshot.withMutableSnapshot {
    dog.name.value = "Fluffy"
  }

  println("stopping")
  observer.stop()

  println("third state change")
  Snapshot.withMutableSnapshot {
    // This change won't trigger the callback.
    dog.name.value = "Fluffy"
  }
}

// Output:
performing initial read pass
dog name: Spot
starting observation
initial state change
something was changed from pass 0
performing next read pass
dog name: Fido
second state change
something was changed from pass 1
performing next read pass
dog name: Fluffy
stopping
third state change
```

We’re still missing a critical component though – snapshot state values can be changed _anywhere_, not just inside snapshots. Remember back to our initial sample – we can change the name directly in the `main` function, and so there must be a way to observe those “top-level” writes, right?

## Nested snapshots

Well, it turns out that _all_ code actually runs in a snapshot – even if there’s no explicitly-created snapshot around it. Snapshots have one other feature that we haven’t covered yet: they nest. If you call `takeMutableSnapshot()` inside the `enter` block of a different snapshot, then when the inner snapshot is applied it will _only_ be applied to the _outer_ snapshot. The outer snapshot must then be applied in turn in order for the changes to continue to propagate up the snapshot hierarchy, and so on up the tree, until the changes are applied to the root snapshot. This root snapshot is called the “global snapshot”, and it’s always open. The global snapshot is a bit of a special case, so before we talk about that, let’s explore snapshot nesting.

Let’s create a nested snapshot called `innerSnapshot` to rename our dog in and see what happens:

```kotlin
Fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  val outerSnapshot = Snapshot.takeMutableSnapshot()
  println("initial name: " + dog.name.value)
  outerSnapshot.enter {
    dog.name.value = "Fido"
    println("outer snapshot: " + dog.name.value)

    val innerSnapshot = Snapshot.takeMutableSnapshot()
    innerSnapshot.enter {
      dog.name.value = "Fluffy"
      println("inner snapshot: " + dog.name.value)
    }
    println("before applying inner: " + dog.name.value)
    innerSnapshot.apply().check()
    println("after applying inner: " + dog.name.value)
  }
  println("before applying outer: " + dog.name.value)
  outerSnapshot.apply().check()
  println("after applying outer: " + dog.name.value)
}

// Output:
initial name: Spot
outer snapshot: Fido
inner snapshot: Fluffy
before applying inner: Fido
after applying inner: Fluffy
before applying outer: Spot
after applying outer: Fluffy
```

Notice the last two lines – the rename to “Fluffy” was not applied to the top-level value until we applied the outer snapshot. This makes sense if you think of applying a snapshot as a state mutation operation. Since state mutations inside snapshots aren’t applied until `apply()` is called, nested snapshots’ applications are just another type of mutation operation that isn’t visible until the outer snapshot is applied.

## The global snapshot

The global snapshot is a mutable snapshot that sits at the root of the snapshot tree. In contrast to regular mutable snapshots, which must be _applied_ to take effect, the global snapshot does not have an “apply” operation – there’s nothing to apply it to. Instead, it can be “advanced”. Advancing the global snapshot is similar to atomically applying it and immediately reopening it – change notifications are sent for all snapshot state values that were modified in the global snapshot since the last advance.

There are three ways to advance the global snapshot:

1. Apply a mutable snapshot to it. In the above example, when `outerSnapshot` is applied, it’s applied to the global snapshot, and the global snapshot is advanced. 
3. Call [`Snapshot.notifyObjectsInitialized`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/Snapshot.Companion#notifyObjectsInitialized()). This sends change notifications for any state values that were changed since the last advance.
2. Call [`Snapshot.sendApplyNotifications()`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/Snapshot.Companion#sendApplyNotifications()
). This is similar to `notifyObjectsInitialized`, but only advances the snapshot if anything actually changed. This function is implicitly called in the first case, whenever any mutable snapshot is applied to the global one.

Because the global snapshot is not created by a `takeMutableSnapshot` call, we can’t pass in read and write observers like normal. Instead, there’s a dedicated function to which installs a callback for when state changes are applied from the global snapshot: [`Snapshot.registerApplyObserver()`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/snapshots/Snapshot.Companion#registerApplyObserver(kotlin.Function2)
).

Let’s use `registerApplyObserver` and `sendApplyNotifications` to rename our dog without an explicit snapshot:

```kotlin
fun main() {
  val dog = Dog()
  Snapshot.registerApplyObserver { changedSet, snapshot ->
    if (dog.name in changedSet) println("dog name was changed")
  }

  println("before setting name")
  dog.name.value = "Spot"
  println("after setting name")

  println("before sending apply notifications")
  Snapshot.sendApplyNotifications()
  println("after sending apply notifications")
}

// Output:
before setting name
after setting name
before sending apply notifications
dog name was changed
after sending apply notifications
```

The Compose runtime uses the global snapshot APIs to coordinate snapshots with the frames it is responsible for drawing for your UI. When something changes a snapshot state value outside of a composition (e.g. a user event like a click fires an event handler that mutates some state), the Compose runtime schedules a `sendApplyNotifications` call to happen before the next frame is drawn. When it’s time to generate the frame, the `sendApplyNotifications` call advances the global snapshot and applies any changes made to it. The resulting change notifications sent from the global snapshot are used to determine which composables need to be recomposed. Compose then takes a mutable snapshot, recomposes those composables inside that snapshot, and finally applies the snapshot. That snapshot application advances the global snapshot again and makes any state changes performed _during_ recomposition visible to code running in the global snapshot – including code running in other threads.

### Multithreading and the global snapshot

The global snapshot has important implications for multithreaded code. It is not uncommon to run code on a background thread without using explicit snapshots. Consider a network call made from a `LaunchedEffect` on the `IO` dispatcher that updates a `MutableState` with the response. This code mutates a snapshot state value, without a snapshot, in a background thread (remember: Compose only wraps _compositions_ in snapshots, and effects are executed outside of compositions). Without snapshots, this would be dangerous – any code reading that state on other threads would see the new value immediately, and if the value changed at the wrong time, could cause a race condition. However, snapshots have a property called [“isolation”](https://en.wikipedia.org/wiki/Snapshot_isolation). 

Within a snapshot on a given thread, no changes made to state values from other threads will be seen until that snapshot is applied. Snapshots are “isolated” from other snapshots. If code needs to operate on some state and wants to ensure that no other threads can mess with it in the meantime, typically that code would use something like a mutex to guard access to its state. However, because snapshots are isolated, it can just take a snapshot instead, and operate on the state inside the snapshot. Then, if other threads mutate the state, the thread with the snapshot will be blissfully unaware of the changes until the snapshot is applied, and vice versa. Any changes made to state inside the snapshot will not be visible to other threads until the snapshot is applied and the global snapshot is automatically advanced.

But what about code running on other threads without an explicit snapshot? That code will immediately see changes from other threads if they decide to apply their snapshots. In database terms, the snapshot system trades consistency for availability. Code that needs consistency needs to take snapshots, but in most cases snapshots can be ignored.



## Conflicting snapshot writes

In the examples up to now we’ve only been taking a single snapshot, but there’s no reason we couldn’t take a few. After all, if Compose is using snapshots to run code in parallel on multiple threads, it’s going to need multiple snapshots.

Let’s try taking a few mutable ones and mutating the name concurrently (we’re still doing everything in a single thread, but the snapshot operations and state mutations are interleaved):

```kotlin
fun main() {
  val dog = Dog()
  dog.name.value = "Spot"

  val snapshot1 = Snapshot.takeMutableSnapshot()
  val snapshot2 = Snapshot.takeMutableSnapshot()

  println(dog.name.value)
  snapshot1.enter {
    dog.name.value = "Fido"
    println("in snapshot1: " + dog.name.value)
  }
  // Don’t apply it yet, let’s try setting a third value first.

  println(dog.name.value)
  snapshot2.enter {
    dog.name.value = "Fluffy"
    println("in snapshot2: " + dog.name.value)
  }

  // Ok now we can apply both.
  println("before applying: " + dog.name.value)
  snapshot1.apply()
  println("after applying 1: " + dog.name.value)
  snapshot2.apply()
  println("after applying 2: " + dog.name.value)
}

// Output:
Spot
in snapshot1: Fido
Spot
in snapshot2: Fluffy
before applying: Spot
after applying 1: Fido
after applying 2: Fido
```

Both `enter` blocks saw their modified value internally, and the first snapshot successfully applied its change, but after applying the second snapshot the name was still “Fido”, not “Fluffy”. To understand what’s going on here, let’s take a closer look at the `apply()` method. It actually returns a value of type `SnapshotApplyResult`. It’s a sealed class that can either be `Success` or `Failure`. If we add print statements around the `apply()` calls, we will see that the first one succeeds, but the second fails. The reason is that there’s an unresolvable conflict between the updates – both snapshots are trying to change the same name value based on the same initial value (“Spot”). Because the second snapshot was executed with the name of “Spot”, the snapshot system can’t assume that the new value, “Fluffy”, is still correct. It either needs to re-run the `enter` block after applying the new snapshot, or be told explicitly how to merge names. It’s the same situation as when you’re trying to merge a git branch that has conflicts – the merge will fail until you resolve the conflicts.

Compose actually has an API for resolving merge conflicts! `mutableStateOf()` takes an optional [`SnapshotMutationPolicy`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/SnapshotMutationPolicy). The policy defines both how to compare values of a particular type (`equivalent`), as well as how to resolve conflicts (`merge`). Some policies come out of the box:

- **`structuralEqualityPolicy`** – Compares objects by using their `equals` methods (`==`), all writes are considered non-conflicting.
- **`referentialEqualityPolicy`** – Compares objects by reference (`===`), all writes are considered non-conflicting.
- **`neverEqualPolicy`** – Treats all objects as unequal, all writes are considered non-conflicting. This should be used, for example, when the snapshot holds an mutable value and this is used to indicate that the value changed in a way not detectable by == or ===. Holding a mutable object in a mutable state object is not safe (as the mutations are not isolated in any way) but useful if the object implementation is not in your control. 

Notice that none of the pre-built policies resolve write conflicts. Conflict resolution is highly use-case-specific, so there’s no reasonable default. On the other hand, sometimes conflicts can be resolved trivially: the [method documentation for the `merge` method](https://developer.android.com/reference/kotlin/androidx/compose/runtime/SnapshotMutationPolicy?hl=en#merge(kotlin.Any,kotlin.Any,kotlin.Any)) includes the example of a counter. If a counter is incremented by two snapshots independently, the merged counter value is the sum of the amounts by which the counter was incremented in both snapshots.

In our example, it doesn’t really make sense to merge names, so instead let’s just include the previous name in the new one:

```kotlin
class Dog {
  var name: MutableState<String> =
    mutableStateOf("", policy = object : SnapshotMutationPolicy<String> {
      override fun equivalent(a: String, b: String): Boolean = a == b

      override fun merge(previous: String, current: String, applied: String): String =
        "$applied, briefly known as $current, originally known as $previous"
    })
}

fun main() {
  // Same as before.
}

// Output:
Spot
in snapshot1: Fido
Spot
in snapshot2: Fluffy
before applying: Spot
after applying 1: Fido
after applying 2: Fluffy, briefly known as Fido, originally known as Spot
```

If the application of a snapshot results in a write conflict that can’t be resolved, the apply operation is considered failed. The only thing left to do at that point is to take a new snapshot and retry the change.

# Conclusion

Compose’s snapshot system powers some of the key features of its design:
1. **Reactivity:** Stateful code is always kept up-to-date, automatically. You don’t need to worry about tracking invalidations or subscriptions – not only as a feature developer, but even as a library developer. You can make your own “reactive” components using snapshots without manually adding dirty flags.
2. **Isolation:** Stateful code can operate on state without worrying about that state being changed out from under it by code running on different threads. Compose can take advantage of this to do tricks that the old View toolkit couldn’t, such as fan out recompositions onto multiple background threads.

And it does all this using plain old Kotlin features – there is no compiler magic involved. Compose does include a compiler plugin, but that plugin is used for other stuff. The snapshot system uses `ThreadLocal`s under the hood, which are a standard concept from Java that have been around for a long time. I might explore how they’re used in a follow-up post, so stay tuned!

If you want to play with the snapshot system without using the rest of Compose, all you need to do is add a dependency on the runtime artifact:
```kotlin
implementation("androidx.compose.runtime:runtime:$composeVersion")
```

Hopefully this pulls the curtain back just far enough that you can start to see how `mutableStateOf` works together with the rest of the Compose infrastructure to monitor and react to state changes. Please let me know if you have any further questions in the comments!

# Further reading

Unfortunately, at the time of this writing, there isn’t much official documentation about this part of Compose. The closest thing I could find is [the “Jetpack Compose Compilation” episode](https://adbackstage.libsyn.com/episode-164-jetpack-compose-compilation) of the official Android podcast, Android Developers Backstage, where Leland and Adam discuss some of the snapshot workings.

The best way to learn how any code works is to read the code. The Compose source code is generally very clean and well-commented, so it’s a really great place to start. There’s a lot more to dig into – this post only covers a subset of the public API for snapshots. Most of the API is in the [`Snapshot.kt` file](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt;l=45?q=Snapshot&sq=&ss=androidx), so that’s a good place to start exploring on your own. If you want to see how some of this API is used in practice, checkout the [implementation of `snapshotFlow`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt;l=733-790;drc=a3551f26005a5a3828677c41779345e379375f0b).

If you really want to dig deep into the theory behind this stuff, here’s a few more leads:
- You could start with the [Wikipedia article on multiversion concurrency control (MVCC)](https://en.wikipedia.org/wiki/Multiversion_concurrency_control). Compose’s snapshot system is an implementation of MVCC. MVCC is usually discussed in the context of databases, but, as the article points out, it also applies to state stored in volatile memory, which is really a kind of database if you squint hard enough.
- There’s also a [comment](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt;l=570;drc=5563346d0db8dd40b336943c823138f53ed227a1) in the `Snapshot` code linking to a PDF of the paper [“Rethinking serializable multiversion concurrency control”](https://arxiv.org/pdf/1412.2324.pdf).
- The counter example mentioned above is an example of something called a “conflict-free data type”, which you can read more about [here](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type).

_Thanks to [Mark Murphy](https://twitter.com/commonsguy), @mattm, and @adammc331 for reviewing this post and providing early feedback, and to [Chuck Jazdzewski](https://twitter.com/chuckjaz) and [Sean McQuillan](https://twitter.com/objcode) for providing extensive technical corrections and teaching me a bunch in the process!_