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
Convenience for making Result
from NSErrorPointer
API
#14
Conversation
Dynamic frameworks aren’t supported on iOS 7.
public func try(f: NSErrorPointer -> BooleanType) -> Result<Bool> { | ||
var error: NSError? | ||
return f(&error) ? success(true) : failure(error ?? defaultError([:])) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one feels like it should be returning NSError?
instead but then it would be inverted in sense. You can recover that via .error()
so I think it should probably stay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be Result<()>
rather than Result<Bool>
? See @BradLarson's code at #10 (comment) for an example of that.
I agree that there are advantages of this over NSError?
. Unit is a legitimate thing to handle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, Result<()>
is much more satisfying 👍
This was EXC_BAD_ACCESSing when I used BooleanType. Since BooleanType can be arbitrarily converted to Bool in f, I opted to just use Bool.
|
} | ||
|
||
func testTryTSuccess() { | ||
XCTAssertEqual(try(makeTryFunction(42 as Int?)) ?? 43, 42) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine with 42
instead of 42 as Int?
but I wanted to ensure that the type demonstrates that it could be nil
to declare the intent of the test.
✅ Ready for re-review 😄 |
bump 😄 |
I'm not quite clear how this is used with a real Cocoa method that takes an I'm also very slightly nervous about calling it try. That feels like it implies things that aren't true here. It doesn't handle exceptions, and it's only meant for things that use the ObjC NSError** pattern. So it feels very tried to ObjC in a way that feels awkward for such a common and general name as "try." I definitely want a good pattern for |
Thanks for the feedback!
Here are usage examples for let result = try { error in NSFileManager.defaultManager().removeItemAtPath(path, error) } // Result<()> let result = try { error in NSData(contentsOfFile: path, options: 0, error: error) } // Result<NSData>
I have no strong attachment to the name let result = Result { String(contentsOfFile: path, error: $0) } // Result<String>
To be fair, nothing in Swift does 😄 |
OK, that usage makes sense. It certainly has a "wow, it just works" quality to it that I like. I don't think it can be an init this way, though, if we're going to parameterize on Error (as noted in #10). But I could just rename it to While Swift doesn't currently handle exceptions, you could certainly write a Having seen this in action, that really is nice… |
I was going to say that I don’t think parameterizing on the error type is a blocker, except that it probably is because the compiler crashes when attempting to write I still prefer the constructor, but failing that, |
Actually, this crash probably rules out a constructor altogether: http://www.openradar.me/19091061 |
I realized that "result()" is probably a bad idea. People use "result" |
Convenience for making `Result` from `NSErrorPointer` API
Oh, good call! I hadn’t thought of that. |
I've been playing with this, and it's raised some interesting things when coupled with parameterizing the error (#10). (I'm currently experimenting with calling it
The problem is that this particular version only works for
Maybe that's ok in several directions. Maybe it's ok that this only works with NSError, since this pattern is really an NSError pattern. I don't know if any other error type would work this way. Maybe it's ok to make a no-result, no-error return be undefined behavior. It is certainly an unusual situation. If it isn't generic, then it can't be a Result method (since you can't restrict methods). Here's how this wound up looking in practice:
I find the
|
Let’s hope not! 😬 Edit: But possibly yes. One might want a similar function for other out-of-band error APIs, e.g.
You sort of can with the use of
Don’t suppose we can use default parameter values for this? e.g.: public func fromOptional<T, E: ErrorType>(f: () -> T?, defaultError: E = E.defaultError(…)) -> Result<T, NSError> {
…
} IIRC the rule for default parameters is that they have to be literals, so probably not. Edit: Perhaps it’d be reasonable to put this in an |
I could still make this work if we forced all errors to conform to some |
Correct, it’s still present; that’s what I was alluding to in my final edit to the above. (Which presumably didn’t make it as far as your inbox, my apologies!) |
I will probably have a proposal based on this in about a week and half. I've been revising my upcoming CocoaConf talk to incorporate these ideas, and in introducing it to real code, I've made several little changes that I think makes it nicer. Here's of my current small-but-not-trivial working example:
|
Out of curiosity, at this point, why is this |
See #10, starting at #10 (comment) for discussion. It is still a open question, though no one has so far suggested actually making the change. The very short version is that Success/Failure is easier to understand than Right/Left. |
Fair enough. I’ve just finally got around to publishing my |
Cocoa API commonly uses
BOOL
or object type returns coupled with anNSError **
parameter where we would like to useResult
instead. This provides a pair of conveniences to produceResult
in those circumstances.This also brings in a deployment target for iOS.