I would strongly prefer the Result<Value>
form with Swift.Error as the error type, with added ways to optionally pull a typed error out of it when desired. e.g. func error<E>()->E?
instead of var error:Error?
.
In my experience, the typed error tends to get in the way of compose-ability, especially between types that aren't supposed to know about each other. Even with Swift.Error being the default generic type, it would stop it from composing with functions that handle the same base type, but don't know the custom errors that something might return... though I guess you could have a map-style function to genericize the error when it conforms to Swift.Error.
I agree with @riley that unwrap() should be called value() throws -> T
. I call what you are calling value toOptional()->T?
, which I believe is clearer. You could also call it something like optionalValue
, but I believe that it should be made clearer that you are turning it into an optional.
@Dante-Broggi had an interesting idea of extending optional syntax here that is worth thinking about. So myResult!
would unwrap or trap in the case of error, and perhaps myResult?
could unwrap to an optional? This would be especially nice with optional chaining: myResult?.methodOnT
.
One useful init form that I have on my Result type that I saw missing here is:
init(_ value:T?, nilError:@autoclosure ()->Swift.Error) {
guard let value = value else {
self = .error(nilError())
return
}
self = .success(value)
}
This allows you to easily turn an optional returning function into a result returning one.
Also missing are these two sugar inits:
init(_ value:T) {
self = .success(value)
}
init(error:Swift.Error) {
self = .error(error)
}
This lets you write Result(2)
instead of Result.success(2)
, which is much more readable IMO. Though, I suppose instead of the first one, you could just make your public init(value: () throws -> Value)
into an autoclosure: public init(_ value: @autoclosure () throws -> Value)
. Then you can do both Result(2)
and Result(try myThrowingFunction())
.