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

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

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

ca.aab #2

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/