Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Created April 14, 2018 12:09
Show Gist options
  • Save chriseidhof/a394989da810a3aacaa333c215b59543 to your computer and use it in GitHub Desktop.
Save chriseidhof/a394989da810a3aacaa333c215b59543 to your computer and use it in GitHub Desktop.
//: [Previous](@previous)
import Foundation
protocol Changable {
associatedtype Change
mutating func apply(_ diff: Change)
}
extension Array: Changable {
enum Change {
case insert(Element, at: Index)
case remove(at: Index)
}
mutating func apply(_ diff: Change) {
switch diff {
case let .insert(element, at: i):
self.insert(element, at: i)
case let .remove(at: i):
self.remove(at: i)
}
}
}
// what does filter do? we want to filter changes!
final class Box<A> where A: Changable {
private(set) var value: A
private var observers: [(Pending<A>) -> ()] = []
init(_ value: A) {
self.value = value
}
func change(_ change: A.Change) {
let old = value
value.apply(change)
for o in observers {
o(Pending(oldValue: old, change: change))
}
}
func map<B: Changable>(_ f: (A) -> (B, (Pending<A>) -> B.Change?)) -> Box<B> {
let (initial, transform) = f(value)
let result = Box<B>(initial)
observers.append { pending in
if let c = transform(pending) {
result.change(c)
}
}
return result
}
func addObserver(_ o: @escaping (Pending<A>) -> ()) {
observers.append(o)
}
}
struct Pending<A: Changable> {
let oldValue: A
let change: A.Change
}
extension Array {
fileprivate func filteredIndex(_ filterCond: (Element) -> Bool, at index: Int) -> Int {
var result = 0
guard index > 0 else { return 0 }
for i in 0..<(index-1) {
if !filterCond(self[i]) { result += 1 }
}
return result
}
func filtered(_ isIncluded: @escaping (Element) -> Bool) -> ([Element], (Pending<[Element]>) -> Change?) {
let result = filter(isIncluded)
return (result, { pending in
switch pending.change {
case let .insert(el, at: i):
guard isIncluded(el) else { return nil }
return .insert(el, at: pending.oldValue.filteredIndex(isIncluded, at: i)) // todo index
case let .remove(at: i):
guard isIncluded(pending.oldValue[i]) else { return nil }
return .remove(at: i) // todo index
}
})
}
}
let x = Box([1,2,3,4])
let y = x.map { $0.filtered { $0 % 2 == 0 }}
y.addObserver {
print($0)
print(y.value)
}
y.value
x.change(.insert(0, at: 4))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment