Skip to content

Instantly share code, notes, and snippets.

@DeFrenZ
Created March 5, 2015 08:34
Show Gist options
  • Save DeFrenZ/32f8cd1fb4550261851c to your computer and use it in GitHub Desktop.
Save DeFrenZ/32f8cd1fb4550261851c to your computer and use it in GitHub Desktop.
FuturePlayground
// Copyright (c) 2014 Rob Rix. All rights reserved.
// MARK: BoxType
/// The type conformed to by all boxes.
public protocol BoxType {
/// The type of the wrapped value.
typealias Value
/// Initializes an intance of the type with a value.
init(_ value: Value)
/// The wrapped value.
var value: Value { get }
}
/// The type conformed to by mutable boxes.
public protocol MutableBoxType: BoxType {
/// The (mutable) wrapped value.
var value: Value { get set }
}
// MARK: Equality
/// Equality of `BoxType`s of `Equatable` types.
///
/// We cannot declare that e.g. `Box<T: Equatable>` conforms to `Equatable`, so this is a relatively ad hoc definition.
public func == <B: BoxType where B.Value: Equatable> (lhs: B, rhs: B) -> Bool {
return lhs.value == rhs.value
}
/// Inequality of `BoxType`s of `Equatable` types.
///
/// We cannot declare that e.g. `Box<T: Equatable>` conforms to `Equatable`, so this is a relatively ad hoc definition.
public func != <B: BoxType where B.Value: Equatable> (lhs: B, rhs: B) -> Bool {
return lhs.value != rhs.value
}
// MARK: Map
/// Maps the value of a box into a new box.
public func map<B: BoxType, C: BoxType>(v: B, f: B.Value -> C.Value) -> C {
return C(f(v.value))
}
// Copyright (c) 2014 Rob Rix. All rights reserved.
/// Wraps a type `T` in a reference type.
///
/// Typically this is used to work around limitations of value types (for example, the lack of codegen for recursive value types and type-parameterized enums with >1 case). It is also useful for sharing a single (presumably large) value without copying it.
public final class Box<T>: BoxType, Printable {
/// Initializes a `Box` with the given value.
public init(_ value: T) {
self.value = value
}
/// The (immutable) value wrapped by the receiver.
public let value: T
/// Constructs a new Box by transforming `value` by `f`.
public func map<U>(f: T -> U) -> Box<U> {
return Box<U>(f(value))
}
// MARK: Printable
public var description: String {
return toString(value)
}
}
/// Result
///
/// Container for a successful value (T) or a failure with an NSError
///
import Foundation
public protocol ErrorType {}
extension NSError: ErrorType {}
public let ErrorFileKey = "LMErrorFile"
public let ErrorLineKey = "LMErrorLine"
/// A success `Result` returning `value`
/// This form is preferred to `Result.Success(Box(value))` because it
// does not require dealing with `Box()`
public func success<T>(value: T) -> Result<T> {
return .Success(Box(value))
}
/// A failure `Result` returning `error`
/// The default error is an empty one so that `failure()` is legal
/// To assign this to a variable, you must explicitly give a type.
/// Otherwise the compiler has no idea what `T` is. This form is preferred
/// to Result.Failure(error) because it provides a useful default.
/// For example:
/// let fail: Result<Int> = failure()
///
private func defaultError(userInfo: [NSObject: AnyObject]) -> NSError {
return NSError(domain: "", code: 0, userInfo: userInfo)
}
private func defaultError(message: String, file: String = __FILE__, line: Int = __LINE__) -> NSError {
return defaultError([NSLocalizedDescriptionKey: message, ErrorFileKey: file, ErrorLineKey: line])
}
private func defaultError(file: String = __FILE__, line: Int = __LINE__) -> NSError {
return defaultError([ErrorFileKey: file, ErrorLineKey: line])
}
public func failure<T>(message: String, file: String = __FILE__, line: Int = __LINE__) -> Result<T> {
return failure(defaultError(message, file: file, line: line))
}
public func failure<T>(file: String = __FILE__, line: Int = __LINE__) -> Result<T> {
return failure(defaultError(file: file, line: line))
}
public func failure<T>(error: ErrorType) -> Result<T> {
return .Failure(error)
}
/// Construct a `Result` using a block which receives an error parameter.
/// Expected to return non-nil for success.
public func try<T>(f: NSErrorPointer -> T?, file: String = __FILE__, line: Int = __LINE__) -> Result<T> {
var error: NSError?
return f(&error).map(success) ?? failure(error ?? defaultError(file: file, line: line))
}
public func try(f: NSErrorPointer -> Bool, file: String = __FILE__, line: Int = __LINE__) -> Result<()> {
var error: NSError?
return f(&error) ? success(()) : failure(error ?? defaultError(file: file, line: line))
}
/// Container for a successful value (T) or a failure with an NSError
public enum Result<T> {
case Success(Box<T>)
case Failure(ErrorType)
/// The successful value as an Optional
public var value: T? {
switch self {
case .Success(let box): return box.value
case .Failure: return nil
}
}
/// The failing error as an Optional
public var error: ErrorType? {
switch self {
case .Success: return nil
case .Failure(let err): return err
}
}
public var isSuccess: Bool {
switch self {
case .Success: return true
case .Failure: return false
}
}
/// Return a new result after applying a transformation to a successful value.
/// Mapping a failure returns a new failure without evaluating the transform
public func map<U>(transform: T -> U) -> Result<U> {
switch self {
case Success(let box):
return .Success(Box(transform(box.value)))
case Failure(let err):
return .Failure(err)
}
}
/// Return a new result after applying a transformation (that itself
/// returns a result) to a successful value.
/// Flat mapping a failure returns a new failure without evaluating the transform
public func flatMap<U>(transform:T -> Result<U>) -> Result<U> {
switch self {
case Success(let value): return transform(value.value)
case Failure(let error): return .Failure(error)
}
}
}
extension Result: Printable {
public var description: String {
switch self {
case .Success(let box):
return "Success: \(box.value)"
case .Failure(let error):
return "Failure: \(error)"
}
}
}
/// Failure coalescing
/// .Success(Box(42)) ?? 0 ==> 42
/// .Failure(NSError()) ?? 0 ==> 0
public func ??<T>(result: Result<T>, defaultValue: @autoclosure () -> T) -> T {
switch result {
case .Success(let value):
return value.value
case .Failure(let error):
return defaultValue()
}
}
// Playground - noun: a place where people can play
private let dispatchQueueLabel = "swift.future.task"
//private let completionTasksQueue = dispatch_queue_create("swift.future.completion", DISPATCH_QUEUE_CONCURRENT)
private let completionTasksQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
enum FutureState<T> {
case Pending
case Completed(Result<T>)
}
private enum FutureTask<T> {
case OnComplete(Result<T> -> ())
case OnSuccess(T -> ())
case OnFailure(ErrorType -> ())
}
class Future<T> {
private(set) var state: FutureState<T> {
didSet {
switch state {
case .Pending:
assertionFailure("A Future cannot be set as .Pending after initialisation.")
break
case .Completed(let result):
enqueuedTasks.map {
self.resolveTask($0, withResult: result)
}
}
}
}
private let queue: dispatch_queue_t!
private var enqueuedTasks: [FutureTask<T>] = []
private init(result resultClosure: () -> Result<T>, executedInQueue queue: dispatch_queue_t?) {
self.state = .Pending
self.queue = queue ?? dispatch_queue_create(dispatchQueueLabel, nil)
dispatch_async(self.queue) {
self.state = .Completed(resultClosure())
}
}
init(result: Result<T>) {
state = .Completed(result)
}
init(value: T) {
state = .Completed(success(value))
}
init(error: ErrorType) {
state = .Completed(failure(error))
}
func onSuccess(task: T -> ()) {
enqueuedTasks.append(.OnSuccess(task))
}
func onFailure(task: ErrorType -> ()) {
enqueuedTasks.append(.OnFailure(task))
}
func onComplete(task: Result<T> -> ()) {
enqueuedTasks.append(.OnComplete(task))
}
var isCompleted: Bool {
switch state {
case .Completed: return true
default: return false
}
}
var result: Result<T>? {
switch state {
case .Completed(let result): return result
default: return nil
}
}
var value: T? {
switch state {
case .Completed(.Success(let value)): return value.value
default: return nil
}
}
var error: ErrorType? {
switch state {
case .Completed(.Failure(let error)): return error
default: return nil
}
}
private func resolveTask(task: FutureTask<T>, withResult result: Result<T>) {
switch (task, result) {
case (.OnComplete(let task), _):
dispatch_async(completionTasksQueue) { task(result) }
case (.OnSuccess(let task), .Success(let value)):
dispatch_async(completionTasksQueue) { task(value.value) }
case (.OnFailure(let task), .Failure(let error)):
dispatch_async(completionTasksQueue) { task(error) }
default: break
}
}
func ready() {
let semaphore = dispatch_semaphore_create(0)
onComplete { result -> () in
dispatch_semaphore_signal(semaphore)
return
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}
func await() -> Result<T> {
ready()
return result!
}
func map<U>(transform: T -> U) -> Future<U> {
return Future<U>(result: { () -> Result<U> in
switch self.state {
case .Pending:
assertionFailure("Executing a Future mapped on a .Pending Future")
return success(transform(self.value!))
case .Completed(.Success(let value)):
return success(transform(value.value))
case .Completed(.Failure(let error)):
return failure(error)
}
}, executedInQueue: queue)
}
func flatMap<U>(transform: T -> Future<U>) -> Future<U> {
return Future<U>(result: { () -> Result<U> in
transform(self.value!).await()
}, executedInQueue: queue)
}
}
func future<T>(task: () -> Result<T>) -> Future<T> {
return Future<T>(result: task, executedInQueue: nil)
}
func future<T>(inQueue queue: dispatch_queue_t, performingTask task: () -> Result<T>) -> Future<T> {
return Future<T>(result: task, executedInQueue: queue)
}
func future<T>(task: () -> T) -> Future<T> {
return Future<T>(result: { success(task()) }, executedInQueue: nil)
}
func future<T>(inQueue queue: dispatch_queue_t, performingTask task: () -> T) -> Future<T> {
return Future<T>(result: { success(task()) }, executedInQueue: queue)
}
func future<T>(task: @autoclosure () -> Result<T>) -> Future<T> {
return Future<T>(result: task, executedInQueue: nil)
}
func future<T>(inQueue queue: dispatch_queue_t, performingTask task: @autoclosure () -> Result<T>) -> Future<T> {
return Future<T>(result: task, executedInQueue: queue)
}
func future<T>(task: @autoclosure () -> T) -> Future<T> {
return Future<T>(result: { success(task()) }, executedInQueue: nil)
}
func future<T>(inQueue queue: dispatch_queue_t, performingTask task: @autoclosure () -> T) -> Future<T> {
return Future<T>(result: { success(task()) }, executedInQueue: queue)
}
import XCPlayground
let foo = future { Array(1 ... 100).map { $0 + 1 } }
foo.onSuccess { value in println("success: \(value)") }
foo.onSuccess { value in println("other success: \(value)") }
foo.onFailure { error in println("failure: \(error)") }
foo.onComplete { result in println("completed: \(result)") }
let bar = foo.map { $0.map { $0 + 2 } }
bar.onSuccess { value in println("map success: \(value)") }
let baz = bar.flatMap { value in future { value.map { $0 + 3 } } }
baz.onSuccess { value in println("flatMap success: \(value)") }
println("started future")
XCPSetExecutionShouldContinueIndefinitely()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment