Live data from Hacker News

The power of Result types in Swift

swiftbysundell.com

31–40 of 77 posts

Re: The power of Result types in Swift

#32
post #27

It's a very well written article. I'm just a bit surprised there is no mention of the origins of this paradigm as it originated in functional languages (Maybe monad in Haskell).

I think it’s closer to Either

Well, Either is a monad. Evolution of this style to avoid divergent nesting depths (The Codoken: http://i.imgur.com/BtjZedW.jpg) is a monad, even in the "railway" style of F#.

Re: The power of Result types in Swift

#33
The tricky part of this approach comes when you extend it. Let's say we have two functions and we'd like to combine them and return a third.

    typealias Handler1 = (Result -> Void)
    typealias Handler2 = (Result -> Void)

    func load(addr: Address, then handler: @escaping Handler1) { ... }
    func lookup(name: string, then handler: @escaping Handler2) { ... }


    // What would the type of Handler3 be?
    func loadDefault(then handler: @escaping Handler3) {
       lookup("default") { result in
           switch result {
           case .success(let address):
               load(address) { result2 in
                   switch result2 {
                       case .success(let data):
                           // Happy path
                       case .failure(let error):
                           // We have a load error for handler3
               }
           case .failure(let outerError):
               // ERROR: We have a lookup error for handler3
           }
        }
     }
     // P.S., don't complain about syntax I really don't know swift :P
Handler3's type is actually the sum of LoadError and IndexError. This is sort of a non-trivial problem for a lot of use cases of Either. Monadic and Railway Either programming typically solve this problem by saying, "Fine well then every single Either/Result in the chain needs to be of the same type, which includes a unified error type." In this article's case, I guess we could appeal to Swift's binding to FoundationKit to lift up NSError? Sure! That'll compile! Maybe there is a Compound Type thing? It will probably compile but it'll shape your handler in weird ways.

It still seems pretty unsatisfying. The point here was to make it easy to work out what errors we should handle, but now we need an explosion of combination types (unique to each call graph in this case, wow!) Modern Haskell, which along with ML pioneered these style of, has some slick type machinery tricks to deal with this that are not well supported yet (even type lists are not a slam dunk because we need something that's order-insensitive). Most folks will be required to make a new ad-hoc sum type (like Result, but EitherError), and then have to pattern match on that to handle errors.

But at the end of the day, even Haskell with all its principle has try/catch statements in IO and an exception hierarchy. In fact, it's considered best practice for production code to avoid giving the appearance that Either can capture all your errors (since it seldom can): https://www.fpcomplete.com/blog/2016/11/exceptions-best-prac...

Which is not to say Either's (and Railway programming's) "Sum-type errors") aren't a substantial improvement over special return values or Common Lisp/Go's product-style return values. They are. But sadly they can't really get you away from exception handling when you start interacting with the "real" world as the examples here do. It's a win for things like HTTP responses or decoding libraries.

Re: The power of Result types in Swift

#34
The reason I started using this pattern is one I didn’t see the article mention:

With legacy style result callbacks, you’ll have both an optional value and an optional error returned - this corresponds to four possible states. If I’m using an API returning this, I have to think about two cases which probably won’t happen (neither an error or value, or both an error and a value). I’d like to think the API I’m using wouldn’t return those states, but since it’s not typed as such then code I’m writing should probably handle those cases gracefully.

By using a result type as per the article, it is well defined that only two states are possible. This pattern is a really nice one, and is a great use of Swift’s associated value enums.

Re: The power of Result types in Swift

#35

The tricky part of this approach comes when you extend it. Let's say we have two functions and we'd like to combine them and return a third. typealias Handler1 = (Result -> Void) typealias Handler2 = (Result -> Void) func load(addr: Address, then handler: @escaping Handler1) { ... } func lookup(name: string, then handler: @escaping Handler2) { ... } // What would the type of Handler3 be? func loadDefault(then handler…

You can improve that code by having a single error type. Either by returning the same error from both handlers or having an error sum type which contains both errors. You probably don't really care about the exact error that can be thrown by either handler, you just need enough information to display a sensible message to the user. NSError may be just enough for that purpose!

After doing that, the type of Handler3 can be had as Result, NSError>, for example. And how do you combine the two results into the Tuple? That's an applicative, I'm sure swift provides combinators to do just that.

Re: The power of Result types in Swift

#36
post #18
post #2

I've seen this referred to as "railway programming", and there's a wealth of explanation over at https://fsharpforfunandprofit.com/rop/ . In particular, the slide deck from the FP eXchange contains a diagram (slide 75 out of 154) with a happy path along the top, and a sad path along the bottom, and at every stage when you `bind` a Result into an existing Result, you have the opportunity to divert into the sad path. N…

It's not exactly the same as railway programming as in Swift it's typically not used to connect functions together but rather to replace separate callbacks with one "result" function and to hard type certain results. One typical case in Objective-C style API's would be: - (void)fetchData whenDone finished: [a block] ifFails failed: [another block] Or: - (void)fetchData whenDone finished: [a block that has nullable Da…

You don't need huge chains.

Also, compare it to the interface of Futures.

Re: The power of Result types in Swift

#37

It seems like a major legacy of practical experience with FP in the 200x and 201x years is going to be result types.

Also, the idea of mapping and folding, which is now everywhere, and is not limited to traditional collections.

(Likely applicatives will take another decade to go mainstream.)

Re: The power of Result types in Swift

#39

The tricky part of this approach comes when you extend it. Let's say we have two functions and we'd like to combine them and return a third. typealias Handler1 = (Result -> Void) typealias Handler2 = (Result -> Void) func load(addr: Address, then handler: @escaping Handler1) { ... } func lookup(name: string, then handler: @escaping Handler2) { ... } // What would the type of Handler3 be? func loadDefault(then handler…

You can improve that code by having a single error type. Either by returning the same error from both handlers or having an error sum type which contains both errors. You probably don't really care about the exact error that can be thrown by either handler, you just need enough information to display a sensible message to the user. NSError may be just enough for that purpose! After doing that, the type of Handler3 ca…

Didn't the post you are responding to say that? I suspect you didn't read past the example.

The problem comes because you have to either use a single error for two parts or deal with a tough explosion of types. No?

Re: The power of Result types in Swift

#40
post #12

While typing is a good way to reduce programmer errors, specially for large scale programs, it's much like autocomplete in a word processor. It's cool to have but also a bad habit to form. The quality of prgrams would improve if such aids were reduced and the programmer was expected to form good programming habits instead.

Maybe a quick spell check would've caught the misspelling of prgrams or maybe more proof reading is the answer. /s :D Types are a tool just like auto spell checking. Why not leverage them to reduce cognitive load?

Amusing example, since my phone's autocorrect seems quite often to increase my cognitive load.
Post reply on HN