From 88932f7e4c8545d4dc913080e2144cc24cd84942 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 28 Dec 2016 01:29:40 +0900 Subject: [PATCH] Add `ResultProtocol.fanout(_:)` as an alternative to `&&&` operator --- Result/ResultProtocol.swift | 10 +++++++- Tests/ResultTests/ResultTests.swift | 40 ++++++++++++++--------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Result/ResultProtocol.swift b/Result/ResultProtocol.swift index 634debf..88bd355 100644 --- a/Result/ResultProtocol.swift +++ b/Result/ResultProtocol.swift @@ -51,6 +51,14 @@ public extension ResultProtocol { ifFailure: Result.failure) } + /// Returns a Result with a tuple of the receiver and `other` values if both + /// are `Success`es, or re-wrapping the error of the earlier `Failure`. + public func fanout(_ other: @autoclosure () -> R) -> Result<(Value, R.Value), Error> + where Error == R.Error + { + return self.flatMap { left in other().map { right in (left, right) } } + } + /// Returns a new Result by mapping `Failure`'s values using `transform`, or re-wrapping `Success`es’ values. public func mapError(_ transform: (Error) -> Error2) -> Result { return flatMapError { .failure(transform($0)) } @@ -119,7 +127,7 @@ infix operator &&& : LogicalConjunctionPrecedence public func &&& (left: L, right: @autoclosure () -> R) -> Result<(L.Value, R.Value), L.Error> where L.Error == R.Error { - return left.flatMap { left in right().map { right in (left, right) } } + return left.fanout(right) } precedencegroup ChainingPrecedence { diff --git a/Tests/ResultTests/ResultTests.swift b/Tests/ResultTests/ResultTests.swift index 072ab07..87a92fc 100644 --- a/Tests/ResultTests/ResultTests.swift +++ b/Tests/ResultTests/ResultTests.swift @@ -17,6 +17,24 @@ final class ResultTests: XCTestCase { XCTAssert(Result(nil, failWith: error) == failure) } + func testFanout() { + let resultSuccess = success.fanout(success) + if let (x, y) = resultSuccess.value { + XCTAssertTrue(x == "success" && y == "success") + } else { + XCTFail() + } + + let resultFailureBoth = failure.fanout(failure2) + XCTAssert(resultFailureBoth.error == error) + + let resultFailureLeft = failure.fanout(success) + XCTAssert(resultFailureLeft.error == error) + + let resultFailureRight = success.fanout(failure2) + XCTAssert(resultFailureRight.error == error2) + } + func testBimapTransformsSuccesses() { XCTAssertEqual(success.bimap( success: { $0.characters.count }, @@ -168,26 +186,6 @@ final class ResultTests: XCTestCase { let result = Result.success("fail").tryMap(tryIsSuccess) XCTAssert(result == failure) } - - // MARK: Operators - - func testConjunctionOperator() { - let resultSuccess = success &&& success - if let (x, y) = resultSuccess.value { - XCTAssertTrue(x == "success" && y == "success") - } else { - XCTFail() - } - - let resultFailureBoth = failure &&& failure2 - XCTAssert(resultFailureBoth.error == error) - - let resultFailureLeft = failure &&& success - XCTAssert(resultFailureLeft.error == error) - - let resultFailureRight = success &&& failure2 - XCTAssert(resultFailureRight.error == error2) - } } final class NoErrorTests: XCTestCase { @@ -269,6 +267,7 @@ extension ResultTests { ("testMapRewrapsFailures", testMapRewrapsFailures), ("testInitOptionalSuccess", testInitOptionalSuccess), ("testInitOptionalFailure", testInitOptionalFailure), + ("testFanout", testFanout), ("testErrorsIncludeTheSourceFile", testErrorsIncludeTheSourceFile), ("testErrorsIncludeTheSourceLine", testErrorsIncludeTheSourceLine), ("testErrorsIncludeTheCallingFunction", testErrorsIncludeTheCallingFunction), @@ -289,7 +288,6 @@ extension ResultTests { // ("testTryProducesSuccessesForOptionalAPI", testTryProducesSuccessesForOptionalAPI), ("testTryMapProducesSuccess", testTryMapProducesSuccess), ("testTryMapProducesFailure", testTryMapProducesFailure), - ("testConjunctionOperator", testConjunctionOperator), ] } }