Skip to content

Instantly share code, notes, and snippets.

@insanehunter
Last active August 29, 2015 14:15
Show Gist options
  • Save insanehunter/ad7251c1f828db5f35c5 to your computer and use it in GitHub Desktop.
Save insanehunter/ad7251c1f828db5f35c5 to your computer and use it in GitHub Desktop.
AccessibleOnQueue: Practical example of Applicative Functor to run composition of functions in background queue
/// Structure holding value that should be accessed and mutated on
/// some dispatch queue.
///
/// Note: changing this to struct triggers a bug that 'forgets' mutations
public class AccessibleOnQueue<T> {
public init(_ value: T) {
_value = value
}
/// Creates a writer that can access and change stored value
public func mutate(f: (inout T) -> ()) -> WriteOnQueue {
return pure({ f(&self._value) })
}
/// Creates a reader that can access stored value
public func get<U>(f: T -> U) -> ReadOnQueue<(), U> {
return pure({ f(self._value) })
}
private var _value: T
}
/// Wrapper for read operation
public struct ReadOnQueue<T, U> {
public init(_ f: T -> U) {
self.mapping = f
}
private let mapping: T -> U
}
/// Wrapper for write operation
public struct WriteOnQueue {
public init(_ f: () -> ()) {
self.mutator = f
}
private let mutator: () -> ()
}
/// Combining operator for reads. Produces a new operation that
/// performs combined read/transform on a value.
infix operator <*> { associativity left }
public func <*><T, U, V>(a: ReadOnQueue<T, U>, b: ReadOnQueue<U, V>) -> ReadOnQueue<T, V> {
return ReadOnQueue({ t in b.mapping(a.mapping(t)) })
}
/// Combining operator for writes. Produces a new operation that
/// performs combined write/transform on a value.
public func <*>(a: WriteOnQueue, b: WriteOnQueue) -> WriteOnQueue {
return WriteOnQueue({ t in a.mutator(); b.mutator() })
}
/// Lifting operator, creates read operation from a regular function on value.
public func pure<T, U>(f: T -> U) -> ReadOnQueue<T, U> {
return ReadOnQueue(f)
}
/// Lifting operator, creates write operation from a regular function on value.
public func pure(f: () -> ()) -> WriteOnQueue {
return WriteOnQueue(f)
}
/// Performs synchronous read operation on a given queue and returns resulting value.
public func readSync<U>(reader: ReadOnQueue<(), U>, # queue: dispatch_queue_t) -> U {
var value: U!
dispatch_sync(queue) {
value = reader.mapping()
}
return value
}
/// Performs asynchronous write operation on a given queue using write barrier.
public func writeAsync(writer: WriteOnQueue, # queue: dispatch_queue_t) {
dispatch_barrier_async(queue) {
writer.mutator()
}
}
@insanehunter
Copy link
Author

Usage example:

let queue = dispatch_queue_create("some queue", DISPATCH_QUEUE_CONCURRENT)
struct ProtectedValues {
    var name: String
    var ids: [Int]
}
var state = AccessibleOnQueue(ProtectedValues(name: "Test", ids: [1, 2]))


func rename(newName: String) -> WriteOnQueue {
    return state.mutate({ (inout s: ProtectedValues) -> () in
        s.name = newName
    })
}
func appendId(id: Int) -> WriteOnQueue {
    return state.mutate({ (inout s: ProtectedValues) in
        s.ids.append(id)
    })
}
func id<T>(x: T) -> T {
    return x
}

let before = readSync(state.get(id) <*> pure({ "Hello \($0.name)" }), queue: queue)
writeAsync(rename("World") <*> appendId(3), queue: queue)
let after = readSync(state.get(id) <*> pure({ "Hello \($0.name)" }), queue: queue)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment