Rxswift: Typed errors

Created on 27 Apr 2016  Â·  5Comments  Â·  Source: ReactiveX/RxSwift

I would like to read about the reasoning for not having typed errors like RAC does. Is there any place I can read about it? RAC's arguments seems pretty sensable to me:

When signals and signal producers are allowed to fail in ReactiveCocoa, the kind of error must be specified in the type system. For example, Signal is a signal of integer values that may fail with an error of type NSError.

More importantly, RAC allows the special type NoError to be used instead, which statically guarantees that an event stream is not allowed to send a failure. This eliminates many bugs caused by unexpected failure events.

In Rx systems with types, event streams only specify the type of their values—not the type of their errors—so this sort of guarantee is impossible.

https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/README.md

And what's stated here has interesting points as well

In ReactiveCocoa, since you “fix” the type when you create a Signal or a SignalProducer, the compiler will complain if you try to send something else. Bottom line: in ReactiveCocoa, the compiler won’t allow you to send a different error than the one you are expecting.

https://www.raywenderlich.com/126522/reactivecocoa-vs-rxswift

Most helpful comment

I can't speak to Kruno's intentions, but typed errors are a matter of preference. Like most things in programming, there isn't an unequivocal right or wrong answer. Take Swift for example: it doesn't have typed errors even though it might make sense. I've personally never found a situation where I needed typed errors, and I can see that flatMapping across several observable types could produce a headache of cascading error types, or a MegaError enum. But again, that's just me – everyone has a difference opinion and preference. If you do need them and want to use RxSwift, you can have an Observable<Result<ValueType, ErrorType>> that produces typed errors for you.

All 5 comments

I can't speak to Kruno's intentions, but typed errors are a matter of preference. Like most things in programming, there isn't an unequivocal right or wrong answer. Take Swift for example: it doesn't have typed errors even though it might make sense. I've personally never found a situation where I needed typed errors, and I can see that flatMapping across several observable types could produce a headache of cascading error types, or a MegaError enum. But again, that's just me – everyone has a difference opinion and preference. If you do need them and want to use RxSwift, you can have an Observable<Result<ValueType, ErrorType>> that produces typed errors for you.

Thanks for pointing out that perspective.

Hi @hfossli ,

Thxn @ashfurrow for answering, was little busy :) I guess I can provide a more elaborate explanation how I see things.

This is a complex question that, like all other things in life, has it's pros and cons. I guess since you've pasted those links, I can give my short summary on the question you were asking.

Since this issue references other projects and their contributors and can potentially provide certain individuals a fruitful playground--and I really don't want to waste time and energy on internet wars--I'm really sorry if I'll need to lock this conversation if something goes wrong with this thread.

This is my present view on this subject and hoping that maybe broader organization shares my opinions, but I haven't verified with them, nor can I speak in their name.

For TLDR folks, here is a quick summary of pros and cons of introducing typed errors for RxSwift 2.0 in Swift 2.0 and rationale why we are considering introducing it for RxSwift 3.0 :))))

I'm not assigning any weight to these pros on cons.

Pros

  • more information about sequences just based on its type

    • reading from the type description can sequence error out

    • reading from the type description in what ways can sequence fail

Cons

  • there is no parametrized error throwing in Swift 2.0
// can't express
func myFunction<MyErrorType>()  throws MyErrorType -> ()
  • that would mean that we would need to introduce an error handling model that is different from the one native to language we’re using
  • that means that we can either reinvent something our own, or add a dependency for a error handling mechanism different then the native one in Swift (choosing a dependency would probably be a better idea)

    • if the Swift error handling model is not sufficient, then we should maybe change that and reach consensus how to change the root cause in the language itself, and not mask the problem by reinventing the wheel.

    • using multiple error handling mechanisms is really bad for the entire ecosystem and the gains aren't that drastic IMHO

  • readability - Swift 2.0 doesn't have generic typealiases so you can't write typealias Observable<E> = ObservableSomething<Element, ErrorType>
  • readability consequence is it would require writing Observable<String, NoError>.just("a") vs Observable.just("a")
  • we would need to introduce additional operators just to resolve impedance mismatch between different error types (hello mapError* family)
  • like @ashfurrow says, having typed errors usually converges to having one global mega error type that is "type safe," but is pretty much useless to determine what are the ways in which specific API call can fail because it contains all possible errors in entire system. Typed error handling converges to untyped when number of errors increases.
  • because of that you would need to add even more noise into your code (those mapError* operators mentioned above)
  • Swift compilation is already pretty fragile, and adding another generic parameter would probably cause even more problems with type inference
  • if it's possible for operator to fail internally, for example flatMapWithIndex when Index turns Int.max, what should the result error be? Should it be ErrorType, some enum that says did flatMapWithIndex fail or has error been propagated to it from source (in RxSwift 3.0 it will have ErrorType probably)
  • all platform and os level errors NSError are dynamic by nature because when new iOS version is released you want your apps to continue working with new version out of the box. Having NSError type in a lot of places instead of ErrorType isn't any improvement IMHO.

The main reason why we are planning to introduce it in RxSwift 3.0 because it is useful to more easily communicate that a sequence can't fail across interface boundaries, and it looks like Swift 3.0 will support parametrized error throwing and generic typealiases, so other drawbacks are remedied if we can define typealias Observable<E> = ObservableSomething<Element, ErrorType> and have the best of both worlds.

There isn't any clear winner as far as I can tell, there are just different tradeoffs, but hoping that with Swift 3.0 we'll be able to support all different usage scenarios.

Probably the most mundane reason why people want those TypedErrors is because of showing alert messages :) But will you want to ever show to you user "There was an error parsing JSON". Probably not.

There are always different ways how to think about systems. If there are some important cases you care about, then perhaps it's not best to model them as errors and use automatic propagation, but as return value enum so you need to manually decide for each caller site what to do in that novel case.

The other types of errors are more platform level failure errors, like network connectivity errors. You probably want to say for a certain class of errors in a type safe way how to present them in alert ways. Maybe creating a protocol like protocol PresentableErrorType { var presentableTitle: String? { get } var presentableMessage: String? { get } } and extending some user presentable errors makes sense, maybe not.

What I'm trying to say is that there are plethora of interesting solutions how to approach common problems and each of them has different tradeoffs.
That README.md is full of sensationalistic statements that aren't precise or true:

In Rx systems with types, event streams only specify the type of their values—not the type of their errors ...

To me Rx is an abstraction of sequence that can potentially fail with an error. That's it.
That's the only thing that is important.
Is that error typed or untyped is not important. It doesn't change anything about the concept. It doesn't add or remove some novel properties to the abstraction itself.

Having a typed error doesn't mean that you are breaking Rx abstraction in any way as far as I can tell.
It is true that so far most implementations have chosen to have untyped error, because implementations are highly dependent on the language they are implemented in and not many languages offer to parametrize on the throw type (that is also valid for Java as far as I can tell, because it has unchecked exceptions).

... so this sort of guarantee is impossible.

let xs = originalSequenceThatCanFail.catchError { _ in
     return Observable.empty()
}

... can xs error out? I know that you are thinking that this is not equivalent to what that says in that README.md, but let me try to elaborate this.

Being able to communicate that sequence doesn't error out is just one of the properties that are useful to communicate across interface boundaries. There are also other properties like:

  • is the abstraction sharing side effects and
  • on which scheduler does it produce elements
  • on which scheduler are subscriptions made
  • maybe you are interested, does it produce only one result? etc ...

We've tried to think about how to solve these compile time guarantees in a general way. You can read more about it here.

I'm not arguing that the solution we are providing is perfect and flawless, but you can write really clean type safe code that way.

Interesting reading. Thank you for sharing your insight!

This could have probably be closed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Z-JaDe picture Z-JaDe  Â·  3Comments

RafaelPlantard picture RafaelPlantard  Â·  3Comments

apoloa picture apoloa  Â·  3Comments

retsohuang picture retsohuang  Â·  3Comments

RobinFalko picture RobinFalko  Â·  3Comments