Slide 1

Slide 1 text

try! Swift Tokyo 2018 #tryswiftconf

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

let trySwift = .success(!)

Slide 4

Slide 4 text

let trySwift: Result??> = .success(!)

Slide 5

Slide 5 text

Result V.S. Result 2018/03/08 try!Swift Tokyo 2018 After talks Yasuhiro Inami / @inamiy

Slide 6

Slide 6 text

// Without error type parameter public enum Result { case success(Value) case failure(Swift.Error) } // V.S. // With error type parameter public enum Result { case success(Value) case failure(Error) }

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Result (V.S. Result) • GOOD • Can contain error "type" (not only error "value"!) • Represents error domain (where error comes from) • Exhaustive error pattern matching • BAD • mapError is required per error domain switching • Nested enum errors

Slide 10

Slide 10 text

What BAD people say: "There is no need to check errors exhaustively" "To check a specific error, just using as? casting is sufficient"

Slide 11

Slide 11 text

What GOOD people say: Error type is NOT just for error handling

Slide 12

Slide 12 text

NoError Uninhabited (empty) error type

Slide 13

Slide 13 text

What is NoError ? • enum NoError: Swift.Error {} • Can't create value (e.g. Never) • NoError is also an Error • Represents "0" of algebraic data type • Zero (bottom) type enriches type representation

Slide 14

Slide 14 text

// Example: ReactiveSwift UI Binding // (Can be used ONLY WHEN `Error == NoError`) static func <~ ( provider: Self, source: Source ) -> Disposable? where Source.Value == Value, Source.Error == NoError { ... } // Binding let alphaSignal: Signal = ... view.reactive.alpha <~ alphaSignal

Slide 15

Slide 15 text

Usage of NoError • Signal • Observable type that never errors out • No need to create another type, e.g. RxSwift.Driver • Result • Represents succcessful value only (Can't be described using Result) • Shouldn't we just use plain Value instead? → NO!

Slide 16

Slide 16 text

Result Value If we have a function of Observable -> Result (e.g. ReactiveSwift.first): In Result world... extension SignalProducer { public func first() -> Result? { ... } }

Slide 17

Slide 17 text

In Result world... extension SignalProducer { // This `self` may error out, so we use `Result?` public func first() -> Result? { ... } } extension SignalProducer where Error == NoError { // This `self` will NEVER errors out (by `where` constraint), // so we don't want to use `Result?` here! public func first() -> Value? { ... } } Different type signature = meaningless overload !"

Slide 18

Slide 18 text

Mathematically speaking... • Result Value • Not exactly the same, but isomorphic (invertible) • For more info, dive to Category Theory • NoError is "0" • Useful for non-error handling, which is also a part of error handling! • Result / Observable are the world without zero

Slide 19

Slide 19 text

Swift Poem: Why I prefer typed error https://gist.github.com/inamiy/ e3f5f7524f1bcdc582c2ff8dbbba58dd

Slide 20

Slide 20 text

let trySwift: Result??> = .success(!)

Slide 21

Slide 21 text

let trySwift: Result = .success(!)

Slide 22

Slide 22 text

Thanks! Yasuhiro Inami @inamiy