[Accepted with Modifications] SE-0235: Add Result to the Standard Library


(John McCall) #41

There should be a toolchain up with Result fairly soon; it would be great if we could get some early testing of this potential incompatibility to see how much of a problem it really is.


(Svein Halvor Halvorsen) #42

Result is a success or a failure.


(Matthew Johnson) #43

I don’t think this is accurate. Result is a value that is often produced by a success or a failure. This distinction is subtle but important and is why I am disappointed by the change from Value / Error to Success / Failure. I think type names should focus on describing the value they represent, they should not focus on describing the process that produced the value. The mistake of Success and Failure is that it emphasizes the process rather than the value that is represented.


(Jordan Rose) #44

There's nothing specific about the standard library here, but yes, Result.Result doesn't work as a way to reference a type named "Result" in a module named "Result", because name lookup prioritizes types over modules right now. EDIT: I don't think this is good behavior but someone would have to find the time to sit down and fix it.

Another way to resolve the ambiguity is to use scoped imports, which are preferred over regular imports: import enum Result.Result.


(Matthew Johnson) #45

This is good to know, thanks for sharing!


(John McCall) #46

Currently, lookup prefers declarations from the current module over imported names. The generalization of that rule would be that declarations from module M are preferred over declarations from modules that M depends on. Since Swift is the unique root in the dependency tree, that would innoculate us against the specific problems of introducing a new top-level type name.


(Jordan Rose) #47

Unfortunately, every module implicitly imports Swift, so that rule doesn't work here!


(John McCall) #48

Why not? It would mean that a Result in a module you import would always hide the Result from the standard library, because you’re importing two different modules but they have a dependency so they have a shadowing relationship, which is what we want (from a source-compatibility perspective).

It’s good that everything depends on Swift in this case.


(Jordan Rose) #49

Hm. I was thinking that the correct rule is "look in every import to see what it thinks 'Result' means, recursively" (and that's roughly what's implemented once you've started looking in modules), but if the correct rule is "if this name is shadowed on any path, ignore it in favor of the shadowing decl", then we do get the desirable source compat behavior. It does make it harder to refer to Swift.Result, though, and I don't like that adding an import can change the meaning of existing type names that way (rather than just adding overloads or making things ambiguous).

We should probably split off discussing changes to module name lookup into a separate thread.


(John McCall) #50

It does make it harder to refer to Swift.Result, though, and I don't like that adding an import can change the meaning of existing type names that way (rather than just adding overloads or making things ambiguous).

I think these are just costs we have to accept.

We should probably split off discussing changes to module name lookup into a separate thread.

That's a reasonable request. But we might have to consider changes here soon if this is a seriously compatibility problem, which it's shaping up to be.


(Dave Abrahams) #51

That is true in the same sense that Int is a value that is often representing a number (e.g. but might have come from C and therefore represent a boolean value). If we had wanted a semantics-free type like Optional, we'd have gone for a type like Either<Left, Right>.

I think type names should focus on describing the value they represent, they should not focus on describing the process that produced the value. The mistake of Success and Failure is that it emphasizes the process rather than the value that is represented.

I argued for Success and Failure because the names Value and Error describe only the type constraints on the corresponding parameter, which is redundant with what's written in the declaration of Result and a waste of potential expressive power. Type parameter and associated type and variable names should describe the role that the named entity plays in context, so that they illuminate code at their use site. Success and Failure do that much better than Value and Error, IMO.


(Sean Heber) #52

Was result() considered as a name for get()? I think that might be a more descriptive name at the point of use.


(Konstantin Sukharev) #53

I would like to see some sort of a guide on how to migrate codebase easily from use of Result<Value> with just one parameter. Maybe it is possible to use typealias or something. If this was discussed earlier it's nice to have a link.


(Dave Abrahams) #55

The former is better code! I'd rather see something more descriptive than value here, like barrelCount or whatever the value represents, but it's better than .success(success). Muffin-man patterns always make it harder for me to think about what code actually means, and again, represent a lost opportunity for expressiveness.


(Jon Shier) #58

Yes, you're mapping the failure value, which is an Error of some kind. In any case, the design is locked.

And the capture value naming is probably a mistake on my part. But I don't think anyone should be looking to the standard library for style advice anyway. :wink:


(Adrian Zubarev) #59

Now that this was mentioned, I personally never thought about it myself. However this feels like a missalignment if we renamed Error to Failure but kept mapError and not made it mapFailure.

Can the core team clarify if it‘s intended or maybe an oversight?


(Dave Abrahams) #60

We might have overlooked something here, IMO.


(Adrian Zubarev) #61

Now we still have a chance to fix this but this has to happen ASAP. mapError and flatMapError were methods from the Value / Error realm but since we decided differently we should align the decision everywhere and make it mapFailure / flatMapFailure.

Good catch by @usr213123!


(Adrian Zubarev) #62

Well I would disagree with that. Sure the stdlib is not the holy grail but there is a ton of good code and techniques in there to learn from. :slight_smile:

Here is just one good example where @Ben_Cohen shows how we can in the future apply techniques already used in stdlib to our own code to improve performance:


(Chéyo Jiménez) #63

I don’t agree that the design is locked but i do agree that .mapError is the name we want because Failure is constrained to Swift.Error