Typed error can be useful in certain cases, especially when accompanied with NoError
type.
For example, in reactive programming, https://github.com/ReactiveCocoa/ReactiveSwift (typed error) allows us to create UI bindings only if Error
is NoError
, i.e.:
static func <~ <Source: BindingSource> (provider: Self, source: Source) -> Disposable?
where Source.Value == Value, Source.Error == NoError { ... }
// example
let alphaSignal: Signal<CGFloat, NoError> = ...
view.reactive.alpha <~ alphaSignal
This makes sense since UI binding only requires value
s to be sent, and not for error
s.
Signal<T, NoError>
clearly defines that this instance will never send error during its lifetime.
On the contrary, in https://github.com/ReactiveX/RxSwift (untyped error) for example, we need some runtime check (e.g. fatalError("don't send error")
) before making a safe(?) UI binding, since Observable<T>
doesn't guarantee it doesn't emit error.
That's why there are many attempts to ease the situation, e.g. Driver
, PublishRelay
, etc, but I don't think there could be any best compile-time solution for this problem without the help of type-system.
(There's ongoing discussion in ReactiveX/RxSwift#1470 for more detail)
Obviously, this can be said for Result<T, E>
type as well.
Result<T, NoError>
will express the success-only data, and this is also useful when there is a function that converts from Signal<T, NoError>
to Result<T, NoError>
, e.g. ReactiveSwift's SignalProducer.single()
.
Without Result<T, NoError>
, the implementation of func single() -> Result<Value, Error>?
will probably be splitted into 2 overloaded methods:
extension SignalProducer {
public func first() -> Result<Value>? { ... }
}
extension SignalProducer where Error == NoError {
// NOTE: We don't want to use `Result<Value>?` as return type
// because we already know it doesn't emit error.
public func first() -> Value? { ... }
}
which will be :sadtroll:.
Slide (in Japanese): Result V.S. Result<T, E> // Speaker Deck
In the talk, I gave the above example code to illustrate:
Result<T>
fails the abstraction of parametric polymorphism by having boring overloaded codeResult<Value, NoError>
is isomorphic toValue
NoError
is important for Non- Error-handling as well