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

Backpedal on hoisting Task.Result out of Task #273

Merged
merged 3 commits into from
Jan 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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