[Revised] SE-0235: Add Result to the Standard Library

(Adrian Zubarev) #122

I strongly agree with Jordan here. KeyPath is also one of the types that really lacks of variance and I would be happy if we could fix this one day. The lack of a general solution should not block the current proposal.

(Pedro José Pereira Vieito) #123

+1 to the revised proposal, specially having Error conforming to Swift.Error.

(Jon Shier) #124

I've created an updated PR (broke the old one trying to rebase).

(Ben Rimmington) #125

The implementation will try body(), so the argument label trying: (instead of catching:) might be more accurate.

(Guillaume Lessard) #126

Either you try for the happy path, or you catch the error path. Half-full, half-empty...

(Jordan Rose) #127

Well, you always try the happy path even when there's an error, so I like "trying" better than "catching".

(Guillaume Lessard) #128

Fair enough. Being a cheerful pessimist, I find the other one more amusing!


I think the tests from the pull request demonstrate some of the consequences of the enum case names here. If you expect people to commonly switch over Results then I think you're going to often end up with code that mixes .error(…), error and Error in ways that make my eyes glaze over. Maybe switching will be rare in practice though, and you will mostly see sequences of flatMaps followed by a call to unwrapped.

(Jon Shier) #130

Turns out I only needed them for one test, but it does show that without the property accessors, use of this Result type will evolve differently than those currently in use, which are used to being able to access the properties.

I should be able to do another update to the PR tonight to deal with the additional review comments.

(Jon Shier) #131

PR updated for the latest comments.

(Morten Bek Ditlevsen) #132

I was very much +1 for the original proposal and even more for the revision.
I do think, like some others, that .success and .failure would be better case names.
To me .value makes me think about the fact that Result is a container for a value. This makes me think about it’s implementation rather than it’s purpose.
So in the same manner that Optional does not have a .value, but a more descriptive .some, I would much prefer that the enum cases exposed the semantics of .success and .failure.

(Brent Royal-Gordon) #133

This is late, so feel free to disregard it.

I still prefer the old case names, but they're not a dealbreaker, and I like pretty much everything else about the revised proposal. Let's do it.

I thought I'd describe a place where I would have used Result recently if it had been available. I'm writing a tool that uses SwiftSyntax and, depending on how it's invoked, might perform one or two passes over the syntax tree. That means the second pass usually won't need to parse the inputs, but sometimes it will. The way I currently handle this is with what amounts to a memoizing cache:

var cachedSourceFiles: [URL: SyntaxSourceFile] = [:]

func sourceFile(at url: URL) throws -> SyntaxSourceFile {
  if cachedSourceFiles[url] == nil {
    cachedSourceFiles[url] = try SyntaxTreeParser.parse(url)
  return cachedSourceFiles[url]!

But I don't really want the behavior this implements. If the first read fails, I don't want it to try again on the second read; I want to get the same error back. Result would let me implement that:

var cachedSourceFiles: [URL: Result<SyntaxSourceFile, Error>] = [:]

func sourceFile(at url: URL) throws -> SyntaxSourceFile {
  if cachedSourceFiles[url] == nil {
    cachedSourceFiles[url] = Result { try SyntaxTreeParser.parse(url) }
  return try cachedSourceFiles[url]!.unwrapped() 

Uses of Result like this one would not go away when async/await are introduced. Sometimes you really do just want to treat the result of an operation—whether success or failure—as a piece of data in its own right.

(Brad Hilton) #134

Also late, but my two cents:

I also prefer the old case names: case let .value(value) doesn't look great and I prefer semantically meaningful names.

I think that making Swift.Error self-conforming is great, it's just too bad we don't have default generics, because I think the overwhelming majority of Result usage is going to be Result<T, Swift.Error>. :expressionless:

(Lance Parker) #135

For concrete uses, I would expect there to be a better variable name one could pick than value (e.g. url if the result wraps a URL struct, etc..)

case let .value(url) looks much better.

One place where you wouldn't have a better choice of names would be if you were implementing methods on Result in an extension, but there will be far less of such code than there would be of the former.

(Chéyo Jiménez) #136

Does self conformance mean that we could do the following?

typealias DefaultResult<Value> = Result<Value, Swift.Error> 

(Adrian Zubarev) #137

It does mean that even though the generic type Error is constrained as follows Error : Swift.Error you can have same-type-requirement to Swift.Error. In other words the compiler will allow Result<Value, Swift.Error> without raising an error. So yes your type alias will be possible.

When default generic type parameter are added to Swift we can fold Result<Value, Swift.Error> to Result<Value>.

(John McCall) #138

This review has been completed, and the Core Team has decided to accept it with modifications. You can read and discuss the revisions here; I will be closing this thread.

(John McCall) #139