Skip to content

Instantly share code, notes, and snippets.

Revisions

  1. lorentey revised this gist Oct 21, 2016. 1 changed file with 3 additions and 5 deletions.
    8 changes: 3 additions & 5 deletions Zero-allocation observers with Generalized Existentials.swift
    Original file line number Diff line number Diff line change
    @@ -40,9 +40,8 @@ extension Source {
    }
    }

    struct ClosureSink<S: Source>: Sink {
    let source: S
    let sink: (S.Value) -> Void
    struct ClosureSink<Value>: Sink {
    let sink: (Value) -> Void

    func receive(_ value: Value) {
    sink(value)
    @@ -55,8 +54,7 @@ class Foo {
    }
    }

    struct FooSink<S: Source where S.Value == String>: Sink {
    let source: S
    struct FooSink: Sink {
    let target: Foo
    let context: Int
    func receive(_ value: String) { target.receive(value, with: context) }
  2. lorentey created this gist Oct 21, 2016.
    79 changes: 79 additions & 0 deletions Zero-allocation observers with Generalized Existentials.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@
    protocol Sink {
    associatedtype Value
    func receive(_ value: Value)
    }

    // This assumes Generalized Existentials are a thing
    // https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials
    typealias AnySink<Value> = Any<Sink where .Value == Value>

    protocol Source {
    associatedtype Value

    func subscribe(_ sink: Sink) -> Int
    func unsubscribe(_ token: Int)
    }

    class Signal<Value>: Source {
    private var _sinks: [Int: AnySink<Value>] = [:]
    private var _nextKey = 0

    func subscribe(_ sink: Sink) -> Int {
    let key = _nextKey
    _sinks[key] = sink
    _nextKey += 1
    return key
    }

    func unsubscribe(_ key: Int) {
    _sinks[key] = nil
    }

    func send(_ value: Value) {
    _sinks.forEach { _, sink in sink.receive(value) }
    }
    }

    extension Source {
    func subscribe(_ sink: @escaping (Value) -> Void) -> Int {
    return subscribe(ClosureSink(source: self, sink: sink))
    }
    }

    struct ClosureSink<S: Source>: Sink {
    let source: S
    let sink: (S.Value) -> Void

    func receive(_ value: Value) {
    sink(value)
    }
    }

    class Foo {
    func receive(_ value: String, with context: Int) {
    print("\(value) from Foo with context \(context)")
    }
    }

    struct FooSink<S: Source where S.Value == String>: Sink {
    let source: S
    let target: Foo
    let context: Int
    func receive(_ value: String) { target.receive(value, with: context) }
    }

    let foo = Foo()

    let signal = Signal<String>()

    let k1 = signal.subscribe { print("\($0) from closure observer") }

    // Note that FooSink fits in 3 words, so AnySink probably won't allocate a box for it.
    let k2 = signal.subscribe(FooSink(source: signal, target: foo, context: 42))

    signal.send("Hello")
    // => Hello from closure observer
    // => Hello from Foo with context 42

    signal.unsubscribe(k1)
    signal.unsubscribe(k2)