Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Last active August 20, 2018 05:50
Show Gist options
  • Select an option

  • Save chriseidhof/0a8b0db1812326b0b0fc4accdbf502fa to your computer and use it in GitHub Desktop.

Select an option

Save chriseidhof/0a8b0db1812326b0b0fc4accdbf502fa to your computer and use it in GitHub Desktop.

Revisions

  1. chriseidhof revised this gist Aug 20, 2018. 1 changed file with 15 additions and 16 deletions.
    31 changes: 15 additions & 16 deletions incremental-with-changes.swift
    Original file line number Diff line number Diff line change
    @@ -65,7 +65,7 @@ final class Var<A, C> where C: Change, C.Value == A {
    observers.append(change)
    }

    func map<B, BChange>(_ f: (A) -> B, _ g: @escaping (A, C) -> BChange) -> Var<B, BChange> where BChange: Change, BChange.Value == B {
    func transform<B, BChange>(_ f: (A) -> B, _ g: @escaping (A, C) -> BChange) -> Var<B, BChange> where BChange: Change, BChange.Value == B {
    let result = Var<B, BChange>(f(value))
    observers.append { (y,z) in
    let bC = g(y,z)
    @@ -77,23 +77,23 @@ final class Var<A, C> where C: Change, C.Value == A {

    protocol Filter: Change {
    associatedtype Element
    func filter(_ condition: (Element) -> Bool, old: Value) -> Self?
    func filter(_ condition: (Element) -> Bool, source: Value) -> Self?
    }

    extension ArrayChange: Filter {
    typealias Element = Value.Element
    func filter(_ condition: (A) -> Bool, old: [A]) -> ArrayChange<A>? {
    func filter(_ condition: (A) -> Bool, source: [A]) -> ArrayChange<A>? {
    return withoutActuallyEscaping(condition) { condition in
    let filtered = old.filter(condition)
    let filtered = source.filter(condition)
    func offset(for i: Int) -> Int {
    let slice = old[0..<i]
    let slice = source[0..<i]
    return slice.count - slice.lazy.filter(condition).count
    }
    switch self {
    case let .append(x) where condition(x): return self
    case let .insert(x, at: i) where condition(x):
    return .insert(x, at: i - offset(for: i))
    case let .remove(at: i) where condition(old[i]):
    case let .remove(at: i) where condition(source[i]):
    return .remove(at: i - offset(for: i))
    default: return nil
    }
    @@ -104,23 +104,15 @@ extension ArrayChange: Filter {
    extension Var where C: Filter, A == [C.Element] {
    func filter(_ condition: @escaping (C.Element) -> Bool) -> Var<A, C> {
    let result = Var<A,C>(value.filter(condition))
    observers.append { (old, change) in
    if let c = change.filter(condition, old: old) {
    observers.append { (source, change) in
    if let c = change.filter(condition, source: source) {
    result.modify(c)
    }
    }
    return result
    }
    }

    let arr = Var<[Int], ArrayChange<Int>>([])

    arr.filter { $0 % 2 == 0 }.observe(initial: { x in
    print("filtered: ", x)
    }, change: { (old, c) in
    print("filtered change", old, c, c.applying(to: old))
    })

    enum Either<A, B> {
    case left(A)
    case right(B)
    @@ -169,6 +161,13 @@ extension SumChange: Change {
    }
    }

    let arr = Var<[Int], ArrayChange<Int>>([])

    arr.filter { $0 % 2 == 0 }.observe(initial: { x in
    print("filtered: ", x)
    }, change: { (old, c) in
    print("filtered change", old, c, c.applying(to: old))
    })
    arr.modify(.append(1))
    arr.modify(.append(2))
    arr.modify(.insert(0, at: 2))
  2. chriseidhof revised this gist Aug 15, 2018. 1 changed file with 57 additions and 3 deletions.
    60 changes: 57 additions & 3 deletions incremental-with-changes.swift
    Original file line number Diff line number Diff line change
    @@ -13,6 +13,14 @@ protocol Change {
    func apply(_ : inout Value)
    }

    extension Change {
    func applying(to value: Value) -> Value {
    var c = value
    apply(&c)
    return c
    }
    }

    struct AtomicChange<A>: Change {
    let replaceWith: A
    func apply(_ value: inout A) {
    @@ -110,11 +118,57 @@ let arr = Var<[Int], ArrayChange<Int>>([])
    arr.filter { $0 % 2 == 0 }.observe(initial: { x in
    print("filtered: ", x)
    }, change: { (old, c) in
    var copy = old
    c.apply(&copy)
    print("filtered change", old, c, copy)
    print("filtered change", old, c, c.applying(to: old))
    })

    enum Either<A, B> {
    case left(A)
    case right(B)
    }

    enum SumChange<A, B, AChange, BChange> where AChange: Change, AChange.Value == A, BChange: Change, BChange.Value == B {
    case changeLeft(AChange)
    case changeRight(BChange)
    case replace(Either<A, B>)
    }

    enum ProductChange<A, B, AChange, BChange> where AChange: Change, AChange.Value == A, BChange: Change, BChange.Value == B {
    case changeFst(AChange)
    case changeSnd(BChange)
    case changeBoth(AChange, BChange)
    }

    extension ProductChange: Change {
    func apply(_ to: inout (A,B)) {
    switch self {
    case .changeFst(let c):
    c.apply(&to.0)
    case .changeSnd(let c):
    c.apply(&to.1)
    case .changeBoth(let c0, let c1):
    c0.apply(&to.0)
    c1.apply(&to.1)
    }
    }
    }

    extension SumChange: Change {
    func apply(_ to: inout Either<A, B>) {
    switch self {
    case .changeLeft(let l):
    guard case var .left(x) = to else { fatalError() }
    l.apply(&x)
    to = .left(x)
    case .changeRight(let r):
    guard case var .right(x) = to else { fatalError() }
    r.apply(&x)
    to = .right(x)
    case .replace(let v):
    to = v
    }
    }
    }

    arr.modify(.append(1))
    arr.modify(.append(2))
    arr.modify(.insert(0, at: 2))
  3. chriseidhof created this gist Aug 15, 2018.
    122 changes: 122 additions & 0 deletions incremental-with-changes.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,122 @@
    //
    // main.swift
    // IncrementalWithChanges
    //
    // Created by Chris Eidhof on 15.08.18.
    // Copyright © 2018 objc.io. All rights reserved.
    //

    import Foundation

    protocol Change {
    associatedtype Value
    func apply(_ : inout Value)
    }

    struct AtomicChange<A>: Change {
    let replaceWith: A
    func apply(_ value: inout A) {
    value = replaceWith
    }
    }

    enum ArrayChange<A>: Change {
    case insert(A, at: Int)
    case remove(at: Int)
    case append(A)

    func apply(_ value: inout [A]) {
    switch self {
    case .append(let x):
    value.append(x)
    case let .remove(at: i):
    value.remove(at: i)
    case let .insert(x, at: i):
    value.insert(x, at: i)
    }
    }
    }

    final class Var<A, C> where C: Change, C.Value == A {
    typealias Observer = (_ old: A, _ change: C) -> ()
    private var value: A
    private var observers: [Observer] = []
    init(_ value: A) {
    self.value = value
    }

    func modify(_ c: C) {
    let old = value
    c.apply(&value)
    for o in observers {
    o(old, c)
    }
    }
    func observe(initial: (A) -> (), change: @escaping Observer) {
    initial(value)
    observers.append(change)
    }

    func map<B, BChange>(_ f: (A) -> B, _ g: @escaping (A, C) -> BChange) -> Var<B, BChange> where BChange: Change, BChange.Value == B {
    let result = Var<B, BChange>(f(value))
    observers.append { (y,z) in
    let bC = g(y,z)
    result.modify(bC)
    }
    return result
    }
    }

    protocol Filter: Change {
    associatedtype Element
    func filter(_ condition: (Element) -> Bool, old: Value) -> Self?
    }

    extension ArrayChange: Filter {
    typealias Element = Value.Element
    func filter(_ condition: (A) -> Bool, old: [A]) -> ArrayChange<A>? {
    return withoutActuallyEscaping(condition) { condition in
    let filtered = old.filter(condition)
    func offset(for i: Int) -> Int {
    let slice = old[0..<i]
    return slice.count - slice.lazy.filter(condition).count
    }
    switch self {
    case let .append(x) where condition(x): return self
    case let .insert(x, at: i) where condition(x):
    return .insert(x, at: i - offset(for: i))
    case let .remove(at: i) where condition(old[i]):
    return .remove(at: i - offset(for: i))
    default: return nil
    }
    }
    }
    }

    extension Var where C: Filter, A == [C.Element] {
    func filter(_ condition: @escaping (C.Element) -> Bool) -> Var<A, C> {
    let result = Var<A,C>(value.filter(condition))
    observers.append { (old, change) in
    if let c = change.filter(condition, old: old) {
    result.modify(c)
    }
    }
    return result
    }
    }

    let arr = Var<[Int], ArrayChange<Int>>([])

    arr.filter { $0 % 2 == 0 }.observe(initial: { x in
    print("filtered: ", x)
    }, change: { (old, c) in
    var copy = old
    c.apply(&copy)
    print("filtered change", old, c, copy)
    })

    arr.modify(.append(1))
    arr.modify(.append(2))
    arr.modify(.insert(0, at: 2))
    arr.modify(.append(3))
    arr.modify(.remove(at: 3))