Skip to content
This repository has been archived by the owner on Aug 29, 2022. It is now read-only.

Commit

Permalink
Merge pull request #273 from bignerdranch/zwaldowski/back-result
Browse files Browse the repository at this point in the history
 Backpedal on hoisting Task.Result out of Task
  • Loading branch information
zwaldowski authored Jan 12, 2019
2 parents d2d4632 + 3ec76c6 commit f6bfee2
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 79 deletions.
14 changes: 10 additions & 4 deletions Sources/Task/ExistentialTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Deferred
//
// Created by Zachary Waldowski on 3/28/16.
// Copyright © 2015-2018 Big Nerd Ranch. Licensed under MIT.
// Copyright © 2015-2019 Big Nerd Ranch. Licensed under MIT.
//

import Dispatch
Expand Down Expand Up @@ -189,6 +189,15 @@ import Deferred.Atomics
/// - seealso: `TaskProtocol`
/// - seealso: `Future`
public final class Task<SuccessValue>: NSObject {
/// A type that represents either a wrapped value or an error, representing the
/// possible return values of a throwing function.
public enum Result {
/// The success value, stored as `Value`.
case success(SuccessValue)
/// The failure value, stored as any error.
case failure(Error)
}

private let future: Future<Result>

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
Expand Down Expand Up @@ -246,9 +255,6 @@ public final class Task<SuccessValue>: NSObject {
}

extension Task: TaskProtocol {
/// A type for returning and propagating recoverable errors.
public typealias Result = TaskResult<SuccessValue>

public func upon(_ executor: Executor, execute body: @escaping(Result) -> Void) {
future.upon(executor, execute: body)
}
Expand Down
31 changes: 4 additions & 27 deletions Sources/Task/ResultRecovery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
// Deferred
//
// Created by Zachary Waldowski on 12/9/15.
// Copyright © 2014-2018 Big Nerd Ranch. Licensed under MIT.
// Copyright © 2014-2019 Big Nerd Ranch. Licensed under MIT.
//

extension TaskResult {
extension Task.Result {
/// Evaluates the `transform` for a success result, passing its unwrapped
/// value as the parameter, to derive a new value.
///
/// Use the `map` method with a closure that produces a new value.
@_inlineable
public func map<NewValue>(_ transform: (Value) throws -> NewValue) -> TaskResult<NewValue> {
public func map<NewValue>(_ transform: (SuccessValue) throws -> NewValue) -> Task<NewValue>.Result {
switch self {
case .success(let value):
do {
Expand All @@ -29,8 +28,7 @@ extension TaskResult {
/// value as the parameter, to derive a new result.
///
/// Use `flatMap` with a closure that itself returns a result.
@_inlineable
public func flatMap<NewValue>(_ transform: (Value) throws -> TaskResult<NewValue>) -> TaskResult<NewValue> {
public func flatMap<NewValue>(_ transform: (SuccessValue) throws -> Task<NewValue>.Result) -> Task<NewValue>.Result {
switch self {
case .success(let value):
do {
Expand All @@ -43,24 +41,3 @@ extension TaskResult {
}
}
}

extension TaskResult {
/// Performs a coalescing operation, returning the result of unwrapping the
/// success value of `result`, or `defaultValue` in case of an error.
@_inlineable
public static func ?? (result: TaskResult<Value>, defaultValue: @autoclosure() throws -> Value) rethrows -> Value {
switch result {
case .success(let value):
return value
case .failure:
return try defaultValue()
}
}

/// Performs a coalescing operation, the wrapped success value `result`, or
/// that of `defaultValue` in case of an error.
@_inlineable
public static func ?? (result: TaskResult<Value>, defaultValue: @autoclosure() throws -> TaskResult<Value>) rethrows -> TaskResult<Value> {
return try result.withValues(ifLeft: { _ in try defaultValue() }, ifRight: TaskResult.success)
}
}
25 changes: 8 additions & 17 deletions Sources/Task/TaskResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,15 @@
// Deferred
//
// Created by Zachary Waldowski on 12/9/15.
// Copyright © 2014-2018 Big Nerd Ranch. Licensed under MIT.
// Copyright © 2014-2019 Big Nerd Ranch. Licensed under MIT.
//

/// A type that represents either a wrapped value or an error, representing the
/// possible return values of a throwing function.
public enum TaskResult<Value> {
/// The success value, stored as `Value`.
case success(Value)
/// The failure value, stored as any error.
case failure(Error)
}

// MARK: - Initializers

extension TaskResult {
extension Task.Result {
/// Creates an instance storing a successful `value`.
@_inlineable
public init(success value: @autoclosure() throws -> Value) {
public init(success value: @autoclosure() throws -> SuccessValue) {
self.init(from: value)
}

Expand All @@ -32,7 +23,7 @@ extension TaskResult {

/// Create an exclusive success/failure state derived from two optionals,
/// in the style of Cocoa completion handlers.
public init(value: Value?, error: Error?) {
public init(value: SuccessValue?, error: Error?) {
switch (value, error) {
case (let value?, _):
// Ignore error if value is non-nil
Expand All @@ -49,7 +40,7 @@ private enum TaskResultInitializerError: Error {
case invalidInput
}

extension TaskResult where Value == Void {
extension Task.Result where SuccessValue == Void {
/// Creates the success value.
@_inlineable
public init() {
Expand All @@ -59,19 +50,19 @@ extension TaskResult where Value == Void {

// MARK: - Compatibility with Protocol Extensions

extension TaskResult: Either {
extension Task.Result: Either {
@_inlineable
public init(left error: Error) {
self = .failure(error)
}

@_inlineable
public init(right value: Value) {
public init(right value: SuccessValue) {
self = .success(value)
}

@_inlineable
public func withValues<Return>(ifLeft left: (Error) throws -> Return, ifRight right: (Value) throws -> Return) rethrows -> Return {
public func withValues<Return>(ifLeft left: (Error) throws -> Return, ifRight right: (SuccessValue) throws -> Return) rethrows -> Return {
switch self {
case let .success(value):
return try right(value)
Expand Down
4 changes: 2 additions & 2 deletions Tests/TaskTests/TaskProgressTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// DeferredTests
//
// Created by Zachary Waldowski on 10/11/18.
// Copyright © 2018 Big Nerd Ranch. Licensed under MIT.
// Copyright © 2018-2019 Big Nerd Ranch. Licensed under MIT.
//

import XCTest
Expand Down Expand Up @@ -122,7 +122,7 @@ class TaskProgressTests: CustomExecutorTestCase {
], timeout: shortTimeout)
}

private func delaySuccessAsFuture<Value>(_ value: @autoclosure @escaping() -> Value) -> Future<TaskResult<Value>> {
private func delaySuccessAsFuture<Value>(_ value: @autoclosure @escaping() -> Value) -> Future<Task<Value>.Result> {
let deferred = Task<Value>.Promise()
afterShortDelay {
deferred.succeed(with: value())
Expand Down
4 changes: 2 additions & 2 deletions Tests/TaskTests/TaskProtocolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Deferred
//
// Created by Zachary Waldowski on 9/26/18.
// Copyright © 2018 Big Nerd Ranch. Licensed under MIT.
// Copyright © 2018-2019 Big Nerd Ranch. Licensed under MIT.
//

import XCTest
Expand All @@ -19,7 +19,7 @@ class TaskProtocolTests: XCTestCase {

func testConditionalFutureInitAmbiguity() {
// This is a compiler-time check only.
typealias Result = TaskResult<Int>
typealias Result = Task<Int>.Result
let deferred = Deferred<Result>()
_ = Future(deferred)
}
Expand Down
28 changes: 3 additions & 25 deletions Tests/TaskTests/TaskResultTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// DeferredTests
//
// Created by Zachary Waldowski on 2/7/15.
// Copyright © 2014-2018 Big Nerd Ranch. Licensed under MIT.
// Copyright © 2014-2019 Big Nerd Ranch. Licensed under MIT.
//

import XCTest
Expand All @@ -23,10 +23,6 @@ class TaskResultTests: XCTestCase {
("testDebugDescriptionFailure", testDebugDescriptionFailure),
("testSuccessExtract", testSuccessExtract),
("testFailureExtract", testFailureExtract),
("testCoalesceSuccessValue", testCoalesceSuccessValue),
("testCoalesceFailureValue", testCoalesceFailureValue),
("testFlatCoalesceSuccess", testFlatCoalesceSuccess),
("testFlatCoalesceSuccess", testFlatCoalesceSuccess),
("testInitializeWithBlockSuccess", testInitializeWithBlockSuccess),
("testInitializeWithBlockError", testInitializeWithBlockError),
("testInitializeWithBlockInitFailure", testInitializeWithBlockInitFailure)
Expand All @@ -47,12 +43,12 @@ class TaskResultTests: XCTestCase {

func testDebugDescriptionSuccess() {
let debugDescription = String(reflecting: aSuccessResult)
XCTAssert(debugDescription.hasSuffix("TaskResult<Swift.Int>.success(42)"))
XCTAssert(debugDescription.hasSuffix("Task<Swift.Int>.Result.success(42)"))
}

func testDebugDescriptionFailure() {
let debugDescription = String(reflecting: aFailureResult)
XCTAssert(debugDescription.hasSuffix("TaskResult<Swift.Int>.failure(TestError.first)"))
XCTAssert(debugDescription.hasSuffix("Task<Swift.Int>.Result.failure(TestError.first)"))
}

func testSuccessExtract() {
Expand All @@ -63,24 +59,6 @@ class TaskResultTests: XCTestCase {
XCTAssertThrowsError(try aFailureResult.extract())
}

func testCoalesceSuccessValue() {
XCTAssertEqual(aSuccessResult ?? 43, 42)
}

func testCoalesceFailureValue() {
XCTAssertEqual(aFailureResult ?? 43, 43)
}

func testFlatCoalesceSuccess() {
let result = aSuccessResult ?? Result.success(84)
XCTAssertEqual(try result.extract(), 42)
}

func testFlatCoalesceFailure() {
let result = aFailureResult ?? Result(success: 84)
XCTAssertEqual(try result.extract(), 84)
}

func testInitializeWithBlockSuccess() {
let result = Result(value: 42, error: nil)
XCTAssertEqual(try result.extract(), 42)
Expand Down
4 changes: 2 additions & 2 deletions Tests/TaskTests/VoidResultTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ class VoidResultTests: XCTestCase {

func testDebugDescriptionSuccess() {
let debugDescription = String(reflecting: aSuccessResult)
XCTAssert(debugDescription.hasSuffix("TaskResult<()>.success()"))
XCTAssert(debugDescription.hasSuffix("Task<()>.Result.success()"))
}

func testDebugDescriptionFailure() {
let debugDescription = String(reflecting: aFailureResult)
XCTAssert(debugDescription.hasSuffix("TaskResult<()>.failure(TestError.first)"))
XCTAssert(debugDescription.hasSuffix("Task<()>.Result.failure(TestError.first)"))
}

func testExtract() {
Expand Down

0 comments on commit f6bfee2

Please sign in to comment.