Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a version of map that converts thrown errors to Failures. #118

Merged
merged 6 commits into from
Dec 9, 2015

Conversation

bwhiteley
Copy link
Contributor

Add a variant of map that handles transforms that throw errors and converts errors to Failures.
This map is constrained to Results with Error types that can be constructed from arbitrary ErrorTypes.

do {
return .Success(try transform(value))
} catch {
return .Failure(Error.errorFromErrorType(error) as! Error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This as! Error is scary.
Can you do instead

do {
  ...
} catch error as Error {
  return .Failure(Error.errorFromErrorType(error)
} catch error {
  // what do we do here? `fatalError` I guess? (which would be equivalent to this implementation)
}

}
catch {
guard let convertedError = Error.errorFromErrorType(error) as? Error else {
fatalError("Unable to convert error type '\(error.dynamicType)' to type '\(Error.self)'")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool

@bwhiteley
Copy link
Contributor Author

What's the verdict on this PR?

guard let convertedError = Error.errorFromErrorType(error) as? Error else {
fatalError("Unable to convert error type '\(error.dynamicType)' to type '\(Error.self)'")
}
return .Failure(convertedError)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this actually behaves like flatMap (but with a restricted parameter type), calling it map seems a bit confusing 😕

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, and would have preferred to call it flatMap. However, that resulted in "ambiguous use..." errors. Do you have a recommendation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/cc @antitypical/result

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spitballing, but: Is there maybe a way to rewrite the current flatMap with rethrows in a way that covers both cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The return type of the transform is quite different, and part of the reason for this PR is to be able to pass in functions that are not aware of Result.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don’t think this is precisely map or flatMap.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally called it tryMap (PR #114), but tried to roll it into flatMap or map based on feedback in that PR. How are we feeling about tryMap?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 on tryMap

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that was my bad, my apologies. I’m 👍 on the name tryMap.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed it to tryMap. Any other concerns?

@robrix
Copy link
Contributor

robrix commented Dec 4, 2015

👍 from me. @NachoSoto?

return .Success(try transform(value))
}
catch {
guard let convertedError = Error.errorFromErrorType(error) as? Error else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused about this, if Error conforms to ErrorTypeConvertible, this is always going to be non-nil and of type Error, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would think so, but

ResultType.swift:82:58: error: 'Self.Error.ConvertibleType' is not convertible to 'Self.Error'; did you mean to use 'as!' to force downcast?
                                let convertedError = Error.errorFromErrorType(error) as Error
                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
                                                                                     as!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a bug.
What if you remove ConvertibleType and change ErrorTypeConvertible:

public protocol ErrorTypeConvertible: ErrorType {
    static func errorFromErrorType(error: ErrorType) -> Self
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does permit the use of a plain as in tryMap, but then I can't make NSError conform to ErrorTypeConvertible, presumably because it's not final.

I boiled it down to a sample that you can tinker with in a Playground.

import Foundation

// For types that can be created from an arbitrary ErrorType
public protocol ErrorTypeConvertible: ErrorType {
    typealias ConvertibleType = Self
    static func errorFromErrorType(error: ErrorType) -> ConvertibleType
}

protocol MyType {
    typealias Error: ErrorType
    var error: Error { get }
}

extension MyType where Error: ErrorTypeConvertible {
    func foo() -> Error {
        return Error.errorFromErrorType(error) as! Error // ** Why does this require `!`? **
    }
}

extension NSError: ErrorTypeConvertible {
    public static func errorFromErrorType(error: ErrorType) -> NSError {
        return error as NSError
    }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably related to https://twitter.com/jckarter/status/672931114944696321.

Given that the cast can never fail I'd just use as! and add a comment to improve this later on.

@NachoSoto
Copy link
Contributor

👍 thanks @bwhiteley! this looks good now :) :shipit:

NachoSoto added a commit that referenced this pull request Dec 9, 2015
Add a version of `map` that converts thrown errors to `Failure`s.
@NachoSoto NachoSoto merged commit 9f2c108 into antitypical:master Dec 9, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants