Slide 1

Slide 1 text

Swi$ 6ͷTyped throwsͱ Swi$ʹ͓͚ΔΤϥʔϋϯυϦϯά ͷશମ૾ΛֶͿ @koher

Slide 2

Slide 2 text

!LPIFS J04%$+BQBOࢀՃྺ 2016:ෆࢀՃ 2017:۩ମྫͱΫΠζͰֶͿɺ4XJGUͷछྨͷΤϥʔͷ࢖͍෼͚ 2018:J04ΤϯδχΞͷͨΊͷɺ4XJGU͔Β1ZUIPOͷϥΠϒϥϦΛ࢖ͬͯػցֶश͢Δํ๏ 2019:)FBSUPG4XJGU 2020:ਓͰΞϓϦΛϦϑΝΫλϦϯάͯ͠ݟ͖͑ͯͨɺ࠷ڧͷJ04ΞϓϦઃܭʹٻΊΒΕΔ͜ͱ 2021:BTZODBXBJU΍BDUPSͰJ04ΞϓϦ։ൃ͕Ͳ͏มΘΔ͔#FGPSF"GUFSͷ۩ମྫͰֶͿ 2022:4XJGU$PODVSSFODZ࣌୅ͷJ04ΞϓϦͷ࡞Γํ 2023:"*࣌୅ʹੜ͖ΔJ04ΤϯδχΞͷา͖ํ ⛔3FKFDUFE 2024:4XJGUͷ5ZQFEUISPXTͱ4XJGUʹ͓͚ΔΤϥʔϋϯυϦϯάͷશମ૾ΛֶͿ ˞ ಺͸ఏग़ͨ͠ϓϩϙʔβϧͷ਺ʢ೥͸GPSUFFʹه࿥͕ͳ͘ਖ਼֬ͳ਺͸ෆ໌ʣ

Slide 3

Slide 3 text

Heart of Swift !LPIFS

Slide 4

Slide 4 text

ࠓ೔࿩͢಺༰ ! Typed throws ! Swi%ͷΤϥʔϋϯυϦϯάͷશମ૾

Slide 5

Slide 5 text

! Typed Throws

Slide 6

Slide 6 text

SE-0413: Typed throws1 • Status: Implemented (Swi3 6.0) 1 h$ps:/ /github.com/swi3lang/swi3-evolu:on/blob/main/proposals/0413-typed-throws.md

Slide 7

Slide 7 text

Untyped throws func parseInt(_ value: String) throws -> Int

Slide 8

Slide 8 text

Untyped throws func parseInt(_ value: String) throws -> Int try parseInt("") // ۭจࣈྻ try parseInt("ABC") // ෆਖ਼ͳϑΥʔϚοτ try parseInt("9999999999999999999") // ൣғ֎

Slide 9

Slide 9 text

Untyped throws enum ParseError: Error { case emptyValue case illegalFormat(String) case outOfBounds(String) }

Slide 10

Slide 10 text

Untyped throws func parseInt(_ value: String) throws -> Int { if value.isEmpty { throw ParseError.emptyValue } guard let number = Int(value) else { if value.contains(/^[-]?\d+$/) { throw ParseError.outOfBounds(value) } else { throw ParseError.illegalFormat(value) } } return number }

Slide 11

Slide 11 text

Untyped throws do { let number = try parseInt(string) print(number) } catch { print(error) // any Error }

Slide 12

Slide 12 text

Untyped throws do { let number: Int = try parseInt(string) print(number) } catch let error as ParseError { switch error { // ParseError case .emptyValue: ... case .illegalFormat(let value): ... case .outOfBounds(let value): ... } }

Slide 13

Slide 13 text

⛔ Errors thrown from here are not handled because the enclosing catch is not exhaus6ve

Slide 14

Slide 14 text

Untyped throws do { let number: Int = try parseInt(string) print(number) } catch let error as ParseError { switch error { // ParseError case .emptyValue: ... case .illegalFormat(let value): ... case .outOfBounds(let value): ... } }

Slide 15

Slide 15 text

Untyped throws do { let number: Int = try parseInt(string) print(number) } catch let error as ParseError { switch error { // ParseError case .emptyValue: ... case .illegalFormat(let value): ... case .outOfBounds(let value): ... } } catch { preconditionFailure("Never reaches here.") }

Slide 16

Slide 16 text

Untyped throws func parseInt(_ value: String) throws -> Int

Slide 17

Slide 17 text

Typed throws func parseInt(_ value: String) throws(ParseError) -> Int

Slide 18

Slide 18 text

Untyped throws do { let number = try parseInt(string) print(number) } catch { print(error) // any Error }

Slide 19

Slide 19 text

Typed throws do { let number = try parseInt(string) print(number) } catch { switch error { // ParseError ... } }

Slide 20

Slide 20 text

Untyped throws do { let number: Int = try parseInt(string) print(number) } catch let error as ParseError { switch error { // ParseError case .emptyValue: ... case .illegalFormat(let value): ... case .outOfBounds(let value): ... } } catch { preconditionFailure("Never reaches here.") }

Slide 21

Slide 21 text

Typed throws do { let number: Int = try parseInt(string) print(number) } catch { switch error { // ParseError case .emptyValue: ... case .illegalFormat(let value): ... case .outOfBounds(let value): ... } }

Slide 22

Slide 22 text

Typed throws͕΄͔ͬͨ͠ଞͷཧ༝ func foo() throws -> Int { // any Error let result: Result = ... switch result { case .success(let value): return value case .failure(let error): throw error // FooError } } • Result, Task ౳ͱͷม׵ͰΤϥʔͷܕ͕ࣦΘΕΔ

Slide 23

Slide 23 text

Typed throwsؔ࿈ͷߏจ౳

Slide 24

Slide 24 text

Typed throws throws(Τϥʔܕ) -> ໭Γ஋ܕ • () ͸ඞਢ • () ͷதʹΤϥʔܕ͸Ұ͔ͭ͠ॻ͚ͳ͍

Slide 25

Slide 25 text

SE-0413: Typed throws1 • Status: Implemented (Swi3 6.0) • Alterna:ves considered • Thrown error type syntax 1 h$ps:/ /github.com/swi3lang/swi3-evolu:on/blob/main/proposals/0413-typed-throws.md

Slide 26

Slide 26 text

΋͠ () ͕ඞਢͰͳ͔ͬͨΒ func foo() throws (E) -> Int { ... } • ͲͪΒͱղऍ͢Ε͹ྑ͍͔ᐆດ • throws E • throws ((E) -> Int)

Slide 27

Slide 27 text

΋͠ () ͕ඞਢͰͳ͔ͬͨΒ func foo() throws bar -> Int { ... } struct bar: Error {} • bar ͕ܕ͔effect͔۠ผͰ͖ͳ͍

Slide 28

Slide 28 text

SE-0413: Typed throws1 • Status: Implemented (Swi3 6.0) • Alterna:ves considered • Mul:ple thrown error types 1 h$ps:/ /github.com/swi3lang/swi3-evolu:on/blob/main/proposals/0413-typed-throws.md

Slide 29

Slide 29 text

΋͠ () ͷதʹෳ਺ͷΤϥʔܕΛॻ͚ͨΒ func foo() throws(BarError, BazError) -> Int • BarError | BazError Λಋೖ͢Δͷͱಉ͡ • Typed throwsͷͨΊʹ΍Δʹ͸ܕγεςϜ΁ͷӨڹ͕େ͖͢ ͗Δ

Slide 30

Slide 30 text

ݸਓతʹ͸Τϥʔܕ͸ҰͭͰྑ͍ͱࢥ͏ • ΤϥʔܕΛෳ਺ࢦఆͰ͖Δͱɺ҆қʹԼ૚ͷΤϥʔΛͦͷ·· throw ͕ͪ͠ func foo() throws(IOError, NetworkError, ...) -> Int • Τϥʔͷܕ͕ҰͭͳΒɺͦͷؔ਺ʹͱͬͯͷΤϥʔͱ͸Կ͔Λ ߟ͑Δ͖͔͚ͬʹͳΔ • Լ૚ͷΤϥʔΛͦͷ·· throw ͢ΔͳΒ any Error Ͱྑ͍

Slide 31

Slide 31 text

Typed throws throws(Τϥʔܕ) -> ໭Γ஋ܕ • () ͸ඞਢ • () ͷதʹΤϥʔܕ͸Ұ͔ͭ͠ॻ͚ͳ͍

Slide 32

Slide 32 text

do do { let number = try parseInt(string) // ParseError print(number) } catch { // ParseError }

Slide 33

Slide 33 text

do do { let number = try parseInt(string) // ParseError try foo(number) // FooError } catch { // ??? }

Slide 34

Slide 34 text

do do { let number = try parseInt(string) // ParseError try foo(number) // FooError } catch { // any Error }

Slide 35

Slide 35 text

do throws do throws(ParseError) { let number = try parseInt(string) // ParseError try foo(number) // ⛔ FooError } catch { // ParseError }

Slide 36

Slide 36 text

throws(any Error) func foo() throws(any Error) -> Int

Slide 37

Slide 37 text

throws(any Error) func foo() throws(any Error) -> Int func foo() throws -> Int // ͜Εͱಉ͡

Slide 38

Slide 38 text

throws(Never) func foo() throws(Never) -> Int

Slide 39

Slide 39 text

throws(Never) func foo() throws(Never) -> Int func foo() -> Int // ͜Εͱಉ͡

Slide 40

Slide 40 text

Result.get // Swift 5 enum Result { func get() throws -> Success }

Slide 41

Slide 41 text

Result.get // Swift 6 enum Result { func get() throws(Failure) -> Success }

Slide 42

Slide 42 text

Result.get // Swift 5 let result: Result = ... do { let value = try result.get() } catch { preconditionFailure("Never reaches here.") }

Slide 43

Slide 43 text

Result.get // Swift 6 let result: Result = ... let value = result.get()

Slide 44

Slide 44 text

rethrows // try͕ෆཁ [2, 3, 5].map { $0 * $0 } // try͕ඞཁ try ["2", "3", "5"].map { try parseInt($0) }

Slide 45

Slide 45 text

rethrows extension Sequence { func map( _ transform: (Element) throws -> T ) rethrows -> [T] }

Slide 46

Slide 46 text

rethrows extension Sequence { func map( _ transform: (Element) throws -> T ) rethrows -> [T] // rethrows͸↓ͷೋͭͱಉ͡Α͏ͳ΋ͷ func map(_ transform: (Element) -> T) -> [T] func map( _ transform: (Element) throws -> T ) throws -> [T] }

Slide 47

Slide 47 text

rethrows // try͕ෆཁ [2, 3, 5].map { $0 * $0 } // try͕ඞཁ try ["2", "3", "5"].map { try parseInt($0) }

Slide 48

Slide 48 text

rethrows extension Sequence { func map( _ transform: (Element) throws -> T ) rethrows -> [T] }

Slide 49

Slide 49 text

Typed throwsʹΑΔ rethrows extension Sequence { func map( _ transform: (Element) throws(E) -> T ) throws(E) -> [T] }

Slide 50

Slide 50 text

Typed throws͸҆қʹ࢖͏΂͖Ͱ͸ͳ͍

Slide 51

Slide 51 text

SE-0413: Typed throws1 • Status: Implemented (Swi3 6.0) • When to use typed throws • Typed throwsΛ҆қʹ࢖͏΂͖Ͱͳ͍ཧ༝ • Ͳ͏͍͏ͱ͖ʹTyped throwsΛ࢖͏΂͖͔ 1 h$ps:/ /github.com/swi3lang/swi3-evolu:on/blob/main/proposals/0413-typed-throws.md

Slide 52

Slide 52 text

Typed throwsΛ҆қʹ࢖͏΂͖Ͱͳ͍ཧ༝ • Τϥʔͷܕ͸มԽ͠΍͍͢ • Τϥʔ͕໢ཏతʹϋϯυϦϯά͞ΕΔ͜ͱ͸গͳ͍

Slide 53

Slide 53 text

Ͳ͏͍͏ͱ͖ʹTyped throwsΛ࢖͏΂͖͔ • Ϟδϡʔϧ΍ύοέʔδͷதʹดͯ͡ΤϥʔϋϯυϦϯά͢Δ Α͏ͳ৔߹ • map ͷ rethrows ΍ Result ͷΑ͏ʹͦΕࣗମ͕ΤϥʔΛੜΈ ग़͞ͳ͍৔߹ • Existen*al͕࢖͑ͳ͍ʗΦʔόʔϔου͕ڐ༰Ͱ͖ͳ͍৔߹

Slide 54

Slide 54 text

Resist the tempta+on to use typed throws1 Typed throwsΛ࢖͏༠࿭ʹ఍߅͍ͯͩ͘͠͞ 1 h$ps:/ /github.com/swi3lang/swi3-evolu:on/blob/main/proposals/0413-typed-throws.md

Slide 55

Slide 55 text

Even with the introduc/on of typed throws into Swi5, the exis/ng (untyped) throws remains the be>er default error-handling mechanism for most Swi5 code.1 typed throws͕Swi.ʹಋೖ͞Εͨͱͯ͠΋ɺطଘͷʢuntypedʣ throws͸ɺ΄ͱΜͲͷSwi.ίʔυʹ͓͍ͯɺґવͱͯ͠ΑΓྑ͍ σϑΥϧτͷΤϥʔϋϯυϦϯάϝΧχζϜͰ͢ɻ 1 h$ps:/ /github.com/swi3lang/swi3-evolu:on/blob/main/proposals/0413-typed-throws.md

Slide 56

Slide 56 text

ݸਓతʹ͸Ϟδϡʔϧ΍ύοέʔδΛӽ͑ͯྑ͍ͱࢥ͏ • ϞϊϨϙͳͲίʔυ͕Ұମͱͯ͠؅ཧ͞Ε͍ͯΔͱ͖ • Ҿ਺΍໭Γ஋ͷܕͱಉఔ౓ͷස౓Ͱ͔͠Τϥʔܕͷมߋ͕ى͜ Βͳ͍ͱ͖ • Result ͷ Failure ͱมΘΒͳ͍

Slide 57

Slide 57 text

ͱ͸͍͑ɺTyped Throws͕׆͖Δέʔε͸ݶఆత • async ͷͱ͖͸࢖͍ͮΒ͍ • CancellationError ͱͲ͏ڞଘ͢Δʁ

Slide 58

Slide 58 text

Typed throwsͷΞϯνύλʔϯ enum AppError { case network(NetworkError) case server(ServerError) case cancellation(CancellationError) } • ⛔ Typed throwsʹ͢ΔͨΊʹ CancellationError Λ case ʹՃ͑ΔͳͲɺSwi0ͷඪ४తͳΤϥʔϋϯυϦϯάͷ׳ྫͱઓ ͏

Slide 59

Slide 59 text

Typed throwsͷΞϯνύλʔϯ func foo() throws { // any Error try bar() // AppError try baz() // OtherError } do { try foo() } catch is CancellationError { // Ωϟϯηϧ͞Εͨ৔߹ͷॲཧ } catch { // ⛔ AppError.calcellation͕ͬͪ͜ʹؚ·ΕΔ }

Slide 60

Slide 60 text

! Swi%ͷΤϥʔϋϯυϦϯάͷ શମ૾

Slide 61

Slide 61 text

Error Handling Ra-onale and Proposal5 • Kinds of error • Propaga0on 5 h$ps:/ /github.com/swi3lang/swi3/blob/main/docs/ErrorHandlingRa

Slide 62

Slide 62 text

Kinds of error • Simple domain errors • Recoverable errors • Universal errors • Logic failures

Slide 63

Slide 63 text

Simple domain errors guard let number = Int(string) else { // ΤϥʔϋϯυϦϯά } • ൃੜ৚͕݅໌֬Ͱ͙͢ʹϋϯυϦϯά͞ΕΔΑ͏ͳΤϥʔ • Τϥʔͷཧ༝͕໌֬ͳͷͰ nil Ͱද͞ΕΔ • ͙͢ʹϋϯυϦϯά͠ͳ͍ͱݪҼෆ໌ʹ • ϋϯυϦϯά͸৚݅෼ذͰे෼

Slide 64

Slide 64 text

Simple domain errors guard let last = array.popLast() else { // ΤϥʔϋϯυϦϯά } guard let max = array.max() else { // ΤϥʔϋϯυϦϯά }

Slide 65

Slide 65 text

Recoverable errors do { try data.write(to: url, options: .atomic) } catch { // ΤϥʔϋϯυϦϯά } • ݪҼ͕ଟذʹ౉ΔΤϥʔͰ Error Ͱද͞ΕΔ • ద੾ͳ৔ॴ·ͰΤϥʔΛ఻೻ͤ͞ݪҼʹԠͯ͡ϋϯυϦϯά • ઐ༻ͷߏจʢ throws ΍ do/try/catch ʣ͕΄͍͠

Slide 66

Slide 66 text

Universal errors Array(repeating: 42, count: .max) // Out of memory • ݪҼͱͳΔՕॴ͕ଟ༷Ͱ͋ΒΏΔՕॴͰى͜ΓಘΔΤϥʔ • ϓϩηεͷதஅɾϝϞϦෆ଍ɾελοΫΦʔόʔϑϩʔͳͲ • ݱঢ়Ͱ͸ඪ४తͳํ๏ͰϋϯυϦϯά͢Δ͜ͱ͸Ͱ͖ͳ͍

Slide 67

Slide 67 text

Logic failures nilValue! // Unexpectedly found nil while unwrapping array[-1] // Index out of bounds Int.max + 1 // Overflow • ίʔυͷޡΓ͕ݪҼͰൃੜ͢ΔΤϥʔ • Forced unwrappingͷࣦഊɾΠϯσοΫεൣғ֎ɾ੔਺ͷΦʔ όʔϑϩʔͳͲͷࣄલ৚݅ҧ൓ͳͲ • ಈతʹϋϯυϦϯά͢ΔͷͰ͸ͳ͘ίʔυΛमਖ਼ͯ͠ରԠ

Slide 68

Slide 68 text

Logic failures func foo(length: Int) { precondition(length >= 0) ... }

Slide 69

Slide 69 text

Logic failures do { let number: Int = try parseInt(string) print(number) } catch let error as ParseError { switch error { // ParseError case .emptyValue: ... case .illegalFormat(let value): ... case .outOfBounds(let value): ... } } catch { preconditionFailure("Never reaches here.") }

Slide 70

Slide 70 text

Simple domain errors / Recoverable errors vs Logic failures

Slide 71

Slide 71 text

Simple domain errors vs Logic failures // Simple domain errors dictionary[key] // ࣦഊ͢Δͱnil // Logic failures array[i] // ࣦഊ͢Δͱ࣮ߦ࣌Τϥʔ

Slide 72

Slide 72 text

Ͳ͏ͯ͠ array[i] ͸ ൣғ֎ͷͱ͖ʹ nil Λฦ͞ͳ͍ͷ͔ʁ

Slide 73

Slide 73 text

array[i] ͕ൣғ֎ʹͳΔଟ͘ͷέʔε͸ίʔυͷޡΓ // i, j͕ൣғ֎ʹͳͬͨ͜ͱ͕Θ͔ͬͯ΋࣮ߦ࣌ʹͰ͖Δ͜ͱ͸ͳ͍ func sort(_ array: inout [Int]) { for i in 0 ..< array.count { for j in i + 1 ..< array.count { if array[j] < array[i] { let t = array[j] array[j] = array[i] array[i] = t } } } }

Slide 74

Slide 74 text

Logic failureͰྑ͍΋ͷΛ Logic failureͱͯ͠ѻ͏͜ͱ͕ॏཁ ʢRecoverableʹ͠ͳ͍ʣ

Slide 75

Slide 75 text

optional! ΋ array[i] ΋มΘΒͳ͍ • ͲͪΒ΋Logic failure • Forced unwrappingΛઈରʹආ͚ΔͳΒ array[i] ΋ආ͚ͳ͍ ͱ੔߹ੑ͕औΕͳ͍

Slide 76

Slide 76 text

optional! ΋ array[i] ΋มΘΒͳ͍ let value = Int(string)!

Slide 77

Slide 77 text

optional! ΋ array[i] ΋มΘΒͳ͍ guard let value = Int(string) else { predonditionFailure(...) }

Slide 78

Slide 78 text

optional! ΋ array[i] ΋มΘΒͳ͍ let value = array[0]

Slide 79

Slide 79 text

optional! ΋ array[i] ΋มΘΒͳ͍ guard let value = array.first else { predonditionFailure(...) }

Slide 80

Slide 80 text

Kinds of error • Simple domain errors • Recoverable errors • Universal errors • Logic failures

Slide 81

Slide 81 text

Propaga'on • Manual propaga,on • Automa,c propaga,on

Slide 82

Slide 82 text

Manual propaga+on func foo() -> Foo? { ... guard let value = Int(string) else { return nil // manual } ... }

Slide 83

Slide 83 text

Manual propaga+on func foo() -> Result { ... switch result { case .success(let value): ... case .failure(let error): return .failure(error) // manual } ... }

Slide 84

Slide 84 text

Automa'c propaga'on func foo() throws -> Foo { ... let value = try bar() ... }

Slide 85

Slide 85 text

Automa'c propaga'on func foo(x: Int, y: Int) throws -> Int { let a = try bar(x) let b = try baz(y) return a + b }

Slide 86

Slide 86 text

Manual propaga+on func foo(x: Int, y: Int) -> Result { bar(x).flatMap { a in baz(y).map { b in a + b } } }

Slide 87

Slide 87 text

ؔ਺ܕϓϩάϥϛϯάͷख๏͕͋Ε͹ Automa'c propaga'on͸ཁΒͳ͍ʁ

Slide 88

Slide 88 text

HaskellͰ͢Β do ه๏Λఏڙ͍ͯ͠Δ

Slide 89

Slide 89 text

do ه๏ foo :: Int -> Int -> Either FooError Int foo x y = do a <- bar x b <- baz y return (a + b)

Slide 90

Slide 90 text

do ه๏͕ͳ͍ͷʹؔ਺ܕͷख๏Ͱ͕Μ͹Δͱ ίʔυͷϊΠζ͕૿͑Δ

Slide 91

Slide 91 text

Swi$ͷΤϥʔϋϯυϦϯά Kind \ Propaga-on Manual Automa-c Simple domain Optional - Recoverable Result throws Universal - Ϋϥογϡ Logic - Ϋϥογϡ

Slide 92

Slide 92 text

ΤϥʔͷݪҼ͸όά ʢίʔυΛमਖ਼͢΂͖໰୊ʣ ΤϥʔͷݪҼ͕໌֬ͰҰ͚ͭͩ ʢݪҼผʹ෼ذ͕ඞཁͳ͍ʣ ΤϥʔͷܕΛࢦఆ͍ͨ͠ ※ ຊ౰ʹඞཁ͔ཁݕ౼ Logic failure precondition౳ Simple domain error Optional ΤϥʔͷܕΛݶఆͯ͠ྑ͍৚݅Λ ຬͨ͢ ΤϥʔΛม਺౳Ͱѻ͏ඞཁ͕͋Δ ※ ຊ౰ʹඞཁ͔ཁݕ౼ ΤϥʔΛม਺౳Ͱѻ͏ඞཁ͕͋Δ ※ ຊ౰ʹඞཁ͔ཁݕ౼ Recoverable / Automatic / Typed throws(FooError) Recoverable / Manual / Typed Result Recoverable / Manual / Untyped Result Recoverable / Automatic / Untyped throws YES NO ΤϥʔͷܕΛݶఆͯ͠ྑ͍৚݅ • Ϟδϡʔϧ΍ύοέʔδͷதʹดͯ͡Τϥʔ ϋϯυϦϯά͢ΔΑ͏ͳ৔߹ • map ͷ rethrows ΍ Result ͷΑ͏ʹͦΕ ࣗମ͕ΤϥʔΛੜΈग़͞ͳ͍৔߹ • Existential͕࢖͑ͳ͍ʗΦʔόʔϔου͕ڐ༰ Ͱ͖ͳ͍৔߹

Slide 93

Slide 93 text

·ͱΊ • Typed throws͸ΤϥʔͷܕΛࢦఆͰ͖ͯศར • ͚Ͳɺ࢖͍Ͳ͜Ζ͸ݶΒΕ͍ͯΔ • throws ͸Recoverable error • ΤϥʔͷछྨΛҙࣝͯ͠࢖͍෼͚Δ