たにしきんぐダム

プログラミングやったりゲームしてます

Essential Effects を読んで Cats Effect を勉強した

Scala Advent Calendar 2021 1日目の記事です。

scala-cli のこと書こうと思ったのですが、ちょっと時間なかったので読書記です!

essentialeffects.dev

typelevel.org

TL;DR

  • Essential Effects という本を読んで
  • Cats Effect という、ScalaIO とか parallel and concurrent なプログラムを勉強したよ
  • とてもわかり易かったのでおすすめです、あと猫がかわいい

The Effect Pattern

Essential Effect 1章の話のメモ

この本ではまず初めに IO モナドを使って副作用をラップしてやることのメリットの解説から始まります。本の中では、そのようなプログラムの方法を Effect Pattern と呼んでおり(多分この本でしか言ってない?)、その principal は

The type of the program should tell us what kind of effects the program will perform, in addition to the type of the value it will produce.

  • 例えば println は、標準出力に引数に与えた値を出力するという副作用を起こす関数だが、そのシグネチャAny => Unit みたいな感じ
    • これだけ見ると何か副作用が起こるかどうかは分からない
  • Any => IO[Unit] ならば、Anyを受け取って、何か動作を行って、その結果として Unit 型の値を返す。ということがシグネチャから分かる。
    • といってもIOだと何かが起こる(何が起こるかは分からん)ので情報量はあまりないように思う。
    • eff とか使うといろんな作用がシグネチャに現れて楽しい気がしますね

If the behavior we want relies upon some externally-visible side effect, we separate describing the effects we want to happen from actually making them happen. We can freely substitute the description of effects until the point we run them

  • 副作用が実際に起こるのを遅延させられる。これにより IO#unsafeRun を実行するまでは副作用が起きないということが分かる
  • 何が嬉しい?
    • compose した effect の実行時エラーハンドリングなんかを、effects の実行部分でまとめられるところとかなのだろうか?

また、本の中ではこれを満たしていたら Effect だ!というチェックリストが書かれており(これは一般的な認識なんだろうか?この本の著者がこう言ってるだけ?)

    1. Does the type of the program tell us
      1. what kind of effects the program will perform; and
      1. what type of value it will produce?
    1. When externally-visible side effects are required, is the effect description separate from the execution?

Scala の Future は - 1 は満たす (a. Future は非同期実行ですよ、b. Future[T] なら T を produce するよ) - しかし、2は満たさない。Future の body は externally-visible side effects だが、その中身は Future が construct されたら即座に実行されるので

ということで Future ではなく cats-effectIO が登場するのであった...

演習問題がたくさん

上に書いたことを読むと理論よりの本のように思えるかも知れないが、演習問題が豊富で例えば以下のようなことが手を動かしながら学ぶことができる

  • IO モナドを自作してみたり
  • Resources を使って外部リソースの auto closing や combine などを試してみたり
  • Deferred を使って同時に実行する複数のエフェクト間の協調について学んだり
  • 著者が Concurrent State Machines と読んでいるパターンを使って(状態が外部から見えない)countdown latchを作ってみたり
  • cats-effect の parMapN という複数のIOをconcurrentに実行するメソッドを step by step で実装することにより、 cats-effect の fiber の fork, join, cancel の挙動を理解するなど
  • 総復習として job queue を作ってみたり
/** - ia と ib の computation を "concurrent" に実行する 
  *  - each result を wait
  *  - ia か ib が fail した場合は他方を cancel
  *  - 2つの結果を f で combine
  */ 
def myParMapN[A, B, C](ia: IO[A], ib: IO[B])(f: (A, B) => C): IO[C] = ???

まとめ

非常に良い本なので cats-effect に限らず IO を使った並列並行処理について学んでみたい人は是非読んでみてください。

著者のEssentialEffectsに関する発表が以下のYoutubeから見れるので興味ある人は見てみてね。

youtu.be