Upgrade to Pro — share decks privately, control downloads, hide ads and more …

kotlin-resultを用いて鉄道志向なエラーハンドリングを試みる

 kotlin-resultを用いて鉄道志向なエラーハンドリングを試みる

ca.aab #2

Avatar for Yoshikane Fumitaka

Yoshikane Fumitaka

March 16, 2023
Tweet

More Decks by Yoshikane Fumitaka

Other Decks in Programming

Transcript

  1. GO BEYOND TOGETHER Chief Creative Officer BABY NAIL EXILE EXILE

    THE SECOND 三代目 J SOUL BROTHERS from EXILE TRIBE GENERATIONS from EXILE TRIBE THE RAMPAGE FANTASTICS from EXILE TRIBE BALLISTIK BOYZ from EXILE TRIBE 劇団EXILE DEEP SQUAD DOBERMAN INFINITY Dream Ami Dream Shizuka Happiness 伶 PKCZ DJ DARUMA m-flo MIYAVI Crystal Kay JAY’ED Leola MABU Girls² iScream Lucky² PSYCHIC FEVER ACTOR MODEL ATHLETE AMAZING COFFEE kotlin-resultを用いて 鉄道志向なエラーハンドリングを 試みる Yoshikane Fumitaka, @black_bracken CA.aab #2
  2. GO BEYOND TOGETHER 自己紹介 R 吉兼 史崇 (わらびF R @black_bracke5

    R CL事業本部でAndroi# R 最近はGlanceでウィジェットを書いています
  3. GO BEYOND TOGETHER Kotlin標準ライブラリのResultのおさらい ˆ 例外をキャプチャし、成功or失敗したことを1クラスで値として表現すx ˆ Result<T> = T

    | Throwablr ˆ エラー側の型をジェネリクスで持たなu ˆ (Androidなどに限らない) Kotlinに向けた、成功と失敗のいずれかの型として有– ˆ kotlinx.coroutinesのContinuation#resumeWithなどで利用されていx ˆ sealed classではない (1.7 時点; Result.Failureをvalueに入れたvalue class‘ ˆ onSuccess, onFailureなどのメソッドを使う前8 ˆ ドメイン固有のエラー状態を表現する必要があるなら、Result<T>の代わりに独 自にsealed classで定義することが推奨されている
  4. GO BEYOND TOGETHER michaelbull/kotlin-result V Result<T, E>型を提供するライブラリ (Either<L, R>h V

    エラー側の型を持g V OkもしくはErr型 (sealedh V 標準のResultと同様のメソッドを持g V 加えてflatMapを持g V エラー型の上限に制約がなq V Exceptionに制限されなq V bindingという機x V 後述
  5. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) s 正常処理に主軸を置いたエラーハンドリングへのアプローq s

    関数型プログラミングの一部を簡単に砕いたもp s 複数のエラー状態も型を保R s Scott Wlaschinが提 s Domain Modeling Made Functionalの著者 s 正常な処理に主軸を置くとž s 手続き的な記述では、各処理毎に問題があればそれぞれエラーハン ドリングを行い、早期リターンや例外の送出を行² s ロジックを正常なルート(= happy path)に集中して記述することで 可読性を高められないだろうか?
  6. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) t 簡単のために以下のような具体例(ユースケース)を考えw ur

    現在のユーザを取得し認可されているか確b –r ユーザーIDをもとにデータを取e Dr 取得したデータを保d vr 取得したデータを返’ t まず手続き的 t エラーハンドリングなし
  7. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) @ エラーハンドリングをしていG @

    本質的な処理よりエラーハンドリ ングの方が多い エラーハンドリンr @ (re) throi @ retura @ etc
  8. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) User#isAuthorized() SampleUsecase#invoke() on

    success on failure RemoteRepository#fetchInfo() LocalRepository#save() Output Input 手続き的
  9. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) User#isAuthorized() SampleUsecase#invoke() on

    success on failure RemoteRepository#fetchInfo() LocalRepository#save() Output Input User#isAuthorized() SampleUsecase#invoke() on success on failure RemoteRepository#fetchInfo() LocalRepository#save() Output Input 手続き的 鉄道志向 エラーが起きても連結できれば良い
  10. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) D 前提: 関数合成についG

    D 複数の関数を組み合わせることで新たな1つの関数に合成する function1(apple): Banana function2(banana): Cherry
  11. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) D 前提: 関数合成についG

    D 複数の関数を組み合わせることで新たな1つの関数に合成する
  12. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) D 前提: 関数合成についG

    D 複数の関数を組み合わせることで新たな1つの関数に合成する fun newFunction(apple): Cherry = function2(function1(apple))
  13. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) p 各メソッドがResultを返すようにすれば、それらを合成して1つのユースケースに出来そ™ p

    関数の連結になるので、正常なルート(=処理の実態)に集中して記述でき– p エラーが発生しても、型から分かるのでバイパスするような形にすればO8 p そのユースケースもResultで返してあとでハンドリング User#isAuthorized() SampleUsecase#invoke() on success on failure RemoteRepository#fetchInfo() LocalRepository#save() Output Input
  14. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) i 実際にこれに合わせてプログラムを書いてみI i

    残念ながらコンパイルは通らなƒ i fetchInfoはUserIdを受け取るのであって、Result<User, E>を受け取る訳ではなƒ i 変換も出来ない
  15. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) i 実際にこれに合わせてプログラムを書いてみI i

    残念ながらコンパイルは通らなƒ i fetchInfoはUserIdを受け取るのであって、Result<User, E>を受け取る訳ではなƒ i 変換も出来ない 型が合わない
  16. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) ‚ 型が合わない理g ‚

    fetchInfo()などのメソッドは、Resultを返すことで出力が2路線に対応し` ‚ しかし入力は1路線(正常値のみ)にしか対応していなh ‚ エラーを受け取り、その時には何もせずただ後ろにエラーを返したh ‚ しかしメソッドすべてが引数にResultを受け取るのは非現実的では? この路線に対応する必要がある Result<R, E> Result<T, E> Result<R, E> T
  17. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) もし正常な値なら  その値をもう1段ネストした形に変形して潰して返す もし異常な値なら

     何もせずそのまま返す もし異常な値なら  何もせずそのまま返す ...ようなメソッドがResultにあれば対応可能 Result<T, E>.func(f: (T) -> Result<R, E>): Result<R, E>
  18. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) G ListのflatMapが同じような機能を持ってい‚ G

    map: (T) -> Rを受け取り、List<T>をList<R>に変w G 空なら何もしなR G flatten: List<List<T>>をList<T>に変w G flatMap: (T) -> List<R>を受け取り、List<T>をList<R>に変換
  19. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) G ListのflatMapが同じような機能を持ってい‚ G

    map: (T) -> Rを受け取り、List<T>をList<R>に変w G 空なら何もしなR G flatten: List<List<T>>をList<T>に変w G flatMap: (T) -> List<R>を受け取り、List<T>をList<R>に変換 G Resultにもありまp G map: (T) -> Rを受け取り、Result<T, E>をResult<R, E>に変w G flatMap: (T) -> Result<R>を受け取り、Result<T, E>を Result<R, E>に変換 G flatMaœ G 入れ子構造に変換して、変換後に潰p G Listであれば空なら何もしなR G ResultであればOk型でなければ何もしなR G (...monadic!)
  20. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) User#isAuthorized() SampleUsecase#invoke() on

    success on failure RemoteRepository#fetchInfo() LocalRepository#save() Output Input
  21. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) f エラー型につい™ f

    今回は例のために文字列にしてい` f 実際はドメイン固有なエラーを表現出来たほうが良r f sealed interfaceでUsecase/Repository等にそのエラーを定y f mapEitherなどを使ってレイヤー毎にエラー型の変換を行i f やり過ぎると無意味なボイラープレートになる
  22. GO BEYOND TOGETHER 鉄道志向 (Railway Oriented Programming) g 実装とエラーハンドリングをより分離するアプロー‘ g

    成功と失敗のレールで処理を連結すy g flatMapでResultの結果を連結して関数を合成すy g エラーの種類も型を保ったまま伝播すy g Androidでの適用範囲 g (← Usecase) ← Repository ← DataSource
  23. GO BEYOND TOGETHER 技術の選定と注意点 m 鉄道指向プログラミングの採用についx m Kotlinのエラーハンドリングにおける銀の弾丸ではな’ m KEEPでは、標準ライブラリとしてROPは採用しな’

    m 不必要なケースで使うとそれは例外の再発明にな2 m 想定できないエラーは例外で扱G m エラーの内容が本当に利用する時に必要か考えるこu m 各エラー毎にドメインモデル(sealed class)が必要かf m 目的と手段を履き違えないよう
  24. GO BEYOND TOGETHER 技術の選定と注意点 d CLで跋扈するデータ型た` d Arrow-kR d Try

    (removed in latest$ d Optio% d Kotlin-stdli# d Result<T> d ライブラリの選定は慎重l d 用途に対して適切e d 特にデータレイヤ(~=ドメイン層)など でも使われる場v d 自前で定義しても良i d KMMへの移行に合わせて排除
  25. LIVE CAST ONDEMAND PROGRAM LIVE STREAMING COMMUNITY ありがとうございました V 参考資d

    V https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/result.m4 V https://github.com/michaelbull/kotlin-resulg V https://github.com/swlaschin/RailwayOrientedProgramminT V https://fsharpforfunandprofit.com/ropX V https://fsharpforfunandprofit.com/posts/recipe-part2X V https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/