Created
April 14, 2018 12:09
-
-
Save chriseidhof/a394989da810a3aacaa333c215b59543 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: [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