> On Mar 30, 2017, at 1:20 PM, Joshua Alvarado via swift-evolution 
> <[email protected]> wrote:
> 
> I think think the gain of functionality isn't there for the addition of this 
> functionality to be added. There are other ways to implement what you are 
> desiring to do without adding it to Swift language.

For instance, wrap your type in something like this:

struct Recorded<Base: RangeReplaceableCollection> {
    init(_ base: Base) {
        self.base = base
        changes = [ Change(newElements: base) ]
    }
    
    fileprivate(set) var base: Base
    fileprivate(set) var changes: [Change]
}

extension Recorded {
    final class Change: Hashable, CustomStringConvertible {
        let subrange: Range<Recorded.Index>?
        let newElements: [Recorded.Iterator.Element]
        
        init<C: Collection>(subrange: Range<Index>? = nil, newElements: C)
            where C.Iterator.Element == Recorded.Iterator.Element
        {
            self.subrange = subrange
            self.newElements = Array(newElements)
        }
        
        static func == (lhs: Change, rhs: Change) -> Bool {
            return lhs === rhs
        }
        
        var hashValue: Int {
            return ObjectIdentifier(self).hashValue
        }
        
        var description: String {
            if let subrange = subrange {
                return "base[\(subrange.description)] = 
\(newElements.description)"
            }
            else {
                return "base = \(newElements.description)"
            }
        }
        
        func apply(to c: inout Recorded) {
            let subrange = self.subrange ?? c.startIndex ..< c.endIndex
            c.base.replaceSubrange(subrange, with: newElements)
            c.changes.append(self)
        }
    }
    
    mutating func apply(_ changes: [Change]) {
        for change in changes {
            change.apply(to: &self)
        }
    }
    
    func newChanges(since older: Recorded) -> [Change] {
        var changes = self.changes
        
        guard let lastChange = older.changes.last,
               let i = changes.index(of: lastChange) else {
            return changes
        }
        
        let overlapRange = 0 ... i
        precondition(
            older.changes.suffix(overlapRange.count) == changes[overlapRange],
            "self includes old changes not present in older"
        )
        
        changes.removeSubrange(0 ... i)
        return changes
    }
}

extension Recorded: RangeReplaceableCollection {
    subscript(_ i: Base.Index) -> Base.Iterator.Element {
        get {
            return base[i]
        }
        set {
            replaceSubrange(i ..< index(after: i), with: [newValue])
        }
    }
    
    func index(after i: Base.Index) -> Base.Index {
        return base.index(after: i)
    }
    
    var startIndex: Base.Index {
        return base.startIndex
    }
    
    var endIndex: Base.Index {
        return base.endIndex
    }
    
    init() {
        self.init(Base())
    }
    
    mutating func replaceSubrange<C>(_ subrange: Range<Base.Index>, with 
newElements: C)
        where C : Collection,
        C.Iterator.Element == Base.Iterator.Element
    {
        let change = Change(subrange: subrange, newElements: newElements)
        change.apply(to: &self)
    }
}

(This is begging for Swift 4's conditional conformance feature, which would 
allow `Recorded` to conform to `RandomAccessCollection` et.al. if the 
underlying type did.)

Now you have a ready-made change list, and all you need to do is write a 
`didSet` that runs `newChanges(since: oldValue)` on the new value and figures 
out what to do with it. That ought to be faster than a full difference 
calculation from scratch on every `didSet`.

-- 
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to