Cannot return a future with a different error type by chaining calls with flatMap #115

Closed
NickAger opened this Issue Feb 10, 2016 · 1 comment

Comments

Projects
None yet
1 participant
@NickAger

To illustrate the problem I've modified one of the methods in the test suite, substituting NSError for NoError

    func testFlatMapByPassingFunction() {
        let e = self.expectation()

        func toString(n: Int) -> Future<String, NSError> {
            return Future<String, NSError>(value: "\(n)")
        }

        let n = 1
        let flatMapped = Future<Int, NoError>(value: n).flatMap(toString)

        flatMapped.onSuccess { s in
            XCTAssertEqual(s, "\(n)", "strings are not equal")
            e.fulfill()
        }

        self.waitForExpectationsWithTimeout(2, handler: nil)
    }

Which result in the error: Cannot invoke 'flatMap' with an argument list of type '((Int) -> Future<String, NSError>)'

@NickAger

This comment has been minimized.

Show comment
Hide comment
@NickAger

NickAger Feb 10, 2016

My apologies, I now see why this isn't possible as the resultant future from one or more chained flatMap calls needs to have some kind of consistent error type to allow errors to propagate through the composition.

I stole the idea of a type erasure 'AnyError' type from #108 pull request:

public struct AnyError : ErrorType {
    public let cause:ErrorType

    public init(cause:ErrorType) {
        self.cause = cause
    }
}

Then previous example I gave becomes:

    func testFlatMapByPassingFunction() {
        let e = self.expectation()

        func toString(n: Int) -> Future<String, AnyError > {
            return Future<String, AnyError >(value: "\(n)")
        }

        let n = 1
        let flatMapped = Future<Int, AnyError >(value: n).flatMap(toString)

        flatMapped.onSuccess { s in
            XCTAssertEqual(s, "\(n)", "strings are not equal")
            e.fulfill()
        }

        self.waitForExpectationsWithTimeout(2, handler: nil)
    }

and all works as expected. If only Swift allowed protocol to be used as type parameters ... :

Using 'ErrorType' as a concrete type conforming to protocol 'ErrorType' is not allowed

Thanks again for a great framework - its dramatically simplified my code.

My apologies, I now see why this isn't possible as the resultant future from one or more chained flatMap calls needs to have some kind of consistent error type to allow errors to propagate through the composition.

I stole the idea of a type erasure 'AnyError' type from #108 pull request:

public struct AnyError : ErrorType {
    public let cause:ErrorType

    public init(cause:ErrorType) {
        self.cause = cause
    }
}

Then previous example I gave becomes:

    func testFlatMapByPassingFunction() {
        let e = self.expectation()

        func toString(n: Int) -> Future<String, AnyError > {
            return Future<String, AnyError >(value: "\(n)")
        }

        let n = 1
        let flatMapped = Future<Int, AnyError >(value: n).flatMap(toString)

        flatMapped.onSuccess { s in
            XCTAssertEqual(s, "\(n)", "strings are not equal")
            e.fulfill()
        }

        self.waitForExpectationsWithTimeout(2, handler: nil)
    }

and all works as expected. If only Swift allowed protocol to be used as type parameters ... :

Using 'ErrorType' as a concrete type conforming to protocol 'ErrorType' is not allowed

Thanks again for a great framework - its dramatically simplified my code.

@NickAger NickAger closed this Feb 10, 2016

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