SE-240's CollectionDifference.Change

Thanks for ccing me!

The core team didn't actually express an opinion about the enum nature of Change (and at the time I wasn't aware of any history), but there is some backstory here.

I've been working on this proposal for about 4 years, and Change was originally coded as an enum because those first versions of the proposal encoded cases that had wildly different associated values. One early version looked like:

enum Change {
  case remove(at: Int)
  case insert(element: ChangeElement, at: Int)
  case move(from: Int, to: Int)
}

This version had a number of major issues that the proposal (and discussion) goes into more deeply, but to summarize: reverting a diff requires remove be able to encode elements, and move makes it impossible to safely apply a diff with a 1-pass enumeration of its changes.

That's all history, but I think it still makes sense to use associated values for Change because, despite their matching types, offsets are not directly interchangeable between instances of insert and remove. To crib from a related conversation in the proposal thread:

In this way, insert and remove behave like different (but extremely related) types.

That leaves element as a truly shared value, and Change could have been more purely represented with a type like:

public enum Change {
    public enum Type {
        case insert(offset: Int, associatedWith: Int?)
        case remove(offset: Int, associatedWith: Int?)
    }

    public let type: Type
    public let element: ChangeElement
}

…but that's also pretty cumbersome to deal with, and is no better at serving your examples.

That all said, I am very sympathetic to the ergonomic issues and don't consider the API to be finished. The curse of writing a new API is that you can't always predict how it will be used (assuming it gets used at all :sweat_smile:), so my main goal for SE-0240 was for the idea of diffing to be incorporated into the standard library in a distilled form that reserved the most flexibility for future improvements.

That totally includes exposing the (currently internal) convenience accessors, and between the standard library and your example here I think there's a strong argument for exposing an offset convenience accessor in the future.

1 Like