pexels-photo-3199399.jpeg

ZIO小史

 
0
このエントリーをはてなブックマークに追加
Kazuki Moriyama
Kazuki Moriyama (森山 和樹)

この記事はA Brief History of ZIOを執筆者のJohn A De Goesさんの許可を取って翻訳したものです。
Thanks a lot to John A De Goes for the great article and allowing me to introduce that in my blog!


後にZIOとなるプロジェクトを私が始めたときの目的はScalaにより良い非同期effectシステムをもたらしたいというものだった。
つまり、実行中のeffectのキャンセル機構や並行性を簡単に実現できるといったHaskellのIOモナドのような素晴らしいものである。

このようなeffect systemを開発する一方、私は仕事でScala開発者たちに関数型プログラミングを教えていた。
教える内容にはfunctorの階層構造のような高階型type classについてや、monad transformer、freeモナドなどが含まれていた。
加えて、世界中のScala開発者(非関数型開発者含む)に講演して回っていて、その中で関数型プログラミングについてのフィードバックを受けた。
特に関数型に対して非友好的なものだ。

教えるという経験、開発者たちとの会話から学んだものは、ScalaでのHaskellスタイルの関数型プログラミングの実用性に対する私の考えを根本から変えた。

現実世界からのフィードバック

生徒たちに向けてライブコーディングするさなか、monad transformerを使用することがいかに辛いか、そしてただstateとerrorをもつ単純なプログラムにmonad transformerを使用することを正当化し得ないことを悟った。

高階型によって型推論が犠牲にされるのを何度も目にし生徒と一緒に恐れおののいた。

毎日疑心暗鬼のScala開発者たちに向けて、'effect-polymophism'による単なるテスタビリティの向上以上の確かなメリットを正当化するのに奮闘した。

自身の、そして他の開発者の経験から学んだことは関数型を愛する人がそうじゃない人に向けてそのメリットを売り込むことがどれほど難しいかということだった。

これらの経験を通じ、もしこのままScalaがJVM上でのHaskellを続けるならば、やるべきことをただ片付けたいより多くの開発者の目を引くものにはなりえないだろうと思うに至った。

この信念がZIOのデザインに影響を与えた。

ZIOの進化

敵に遭遇すれば計画は必ず変わる

— Helmuth von Moltke the Elder, 1871

HaskellをScalaの上で行うに際しての辛さ、満足のいかない結果に面するにあたり、たくさんの関数型開発者がScalaからHaskellに移っていった(例えばTony Morris, Sam Halliday, Emily Pillmoreなどの友人たち)。
私は以下の順応を施すことを決めた。

  • 数え切れないほどの開発者がEitherTに苦しむのを見て私はZIOのerror型をThrowableに固定せずポリモーフィックにすることで使いづらいEitherTを使わずに済むと考えた。結果には満足だった。なぜなら使いやすいだけでなく、コンパイル時にコードのどの部分が失敗する可能性があるか否かを判断することができるようになったからだ。これは生産性と正確さを大きく発展させた。
  • 小さな実験が成功に終わったあと、私とco-contributorのWiem Zine Elabidineはdeclaration-site varianceを使用するという攻めたリファクタリングを行った。これは大規模なもので、かつ非常に議論を呼びたくさんのネガティブなフィードバックを受けた。ScalazとCatsは変位指定は悪だ、常に避けるべきという哲学を持っている。しかしリファクタリングはZIOの使用に関して計り知れないほどの喜びをもたらしてくれた。type annotationなしにすべてが美しく推論されたのだ。それからは型推論を最大限活用するために変位指定の強迫観念に駆られたほどだ。
  • 私はHaskellの慣習を壊し、データ型とオペレータの名前を変更した。例えばpoint/pureをZIO.succeedに変更した。これはたくさんのネガティブなフィードバックを引き起こし、最高潮に達したのはZIO.foreachについてだった(*訳注: foreachといえばUnitを返す副作用処理をイメージするが、ZIO.foreachはtraverseに値する)。私の信条は専門用語が役に立たないというものではない。それはよく訓練されたプロの間では必要不可欠だ。しかしその専門領域の知識への依存を排除することは新しい開発者のオンボーディングを容易にする。
  • ドキュメントでは開発者が面する実践的な部分を強調した。いわゆる'lack of purity'で悩む開発者はいないと思っている。彼らのペインポイントは非同期プログラミング、リソース管理の安全性、並行性、テスタビリティにある。このようなZIOの現在のマーケティング焦点は純粋関数型IOモナドから非同期・並行ライブラリへと舵を少し傾けたときに生まれた。

同時にfiberやpure(かつ自動的な)キャンセル機構などのZIO特化の機能はCats Effectに影響を及ぼし始めた。
Cats Effectの多くの変更はZIOの1.0 releaseで実装されたものだと強く主張したい(上で挙げた論点のため1.0 releaseは根本的に異なっている)。

Cats Effectとの乖離

型付きのエラーによってCats Effectの型クラスの階層構造と幾分か距離ができた。
静的にプログラム内のエラーの振る舞いについて知りたいときにはCats Effectの型クラスたちを使用することはできない。
なぜならそれらはポリモーフィックなエラー型をサポートしてないからだ。

加えて、Cats EffectやCats、その他のライブラリには存在しないZIOのコンビネータが実装されていくに従ってCats Effectの機能に縛られることが次第に辛くなっていった。

これらの広がっていく乖離はZIOのアーリーアダプターが型クラスを介さずに直接ZIOを使用する一因になった。

ZIO Environment

初期のZIOは型付けられたerrorのみを持っていた。
しかし2018年の後半にテストするのに複雑な仕掛けが必要な恐ろしいほど歪んだクラスと格闘していた際に一つのアイデアを思いついた。
それはwithで繋がれた複数の合成されたeffectが必要であることを推論するためにScalaの反変を利用できないかということだった。

プロトタイプを100行ほどで実装した後、興奮しながらWiemとそれを共有し「歴史が変わるぞ!」と叫んでいた。
残念ながらプロトタイプでは3つ目の型引数が必要になった(*訳注: ZIO[R, E, A]のRのこと)。
しかし一方でeffectfulなプログラムを完璧に型推論しながらテストすることができ、型クラスもimplicitも高階型も圏論も何一つとして必要としなかった。

2ヶ月かけてWiemと私は何千行ものコードをリファクタして3つ目の型引数をZIOに加えた。
この瞬間がZIOの誕生だ。
エラーと成功を上に、依存を下に向けて伝播させることができるeffect。
今までに書かれたすべての関数型プログラムが必要としていた2つのことである。

tagless-finalのただ一つの確かで有益なメリットであるテスタビリティを得るためにZIO environmentを導入したつもりだった。
しかしすぐにすでにZIOに存在していたプリミティブたち(Refなど)とR型パラメタを組み合わせることでstateやwriter effectをモデリングできることに気がついた。
そして主要なtransformer(StateTやReaderT、WriterT、ExceptTなどのmonad transformer)などはすでにZIOに組み込まれていたため、monad transformerはもう必要なかった。

意識的な挑戦なしにZIOはmonad transformer(とオプション的にtagless-final)の代替手段となった。
そしてScalaでのmonad transformerでは不可能な素晴らしいパフォーマンスと完璧な型推論、最低限の前提知識ですむ代替手段である。

ZIOは専門用語も型クラスもimplicitも高階型も圏論も必要ない今までと違った関数型プログラミングを導出したのである。

ZIO & Cats Effect

これらの達成事項はあるにせよ、私はZIOをCats Effectの競争相手だとは思っていない。
私はCats Effectを異なるeffectを型クラスを通じて相互運用することに重きをおいたプロジェクトだと考えており、故にCats IOは責務の衝突を避けるためにも別プロジェクトに分けるべきだとさえ考えている。

ZIOをCats Effectの型クラスたちの最良の実装であることを皆に知ってもらうためにZIOとCats Effectのシナジーについての記事を書いた。
この中でCats IOが抱える問題をいくつかZIOが改善したことを書いている。

例えば以下のようなものだ。

  • evalOnの実装が非同期なshift間でうまく動いておらず、またエラーが適切に振る舞っていない
  • 停止処理に対しての決めの細かい制御の欠如
  • ゾンビfiberはそれにjoinしてきたいかなるfiberの実行をも永久に止めてしまい、リソース管理の安全性の保証が失われる
  • 非同期effectの呼び出し元スレッドプールでの再開にまつわる問題
  • などなど

シナジーもあるが、表現力に関するZIOとCats Effectの型クラスの間の差が広がりが非常に心配だった。

ZIOはeffect typeをポリモーフィックなエラー、伝播可能なコンテキストで構成していた。
更にはそれは停止処理やエラーハンドリング、コンテキストシフト等のセマンティクスもブラッシュアップしていた。
しかしCats Effectの型クラスたちは一つの型パラメタを取ることに固執し、理解の難しいエッジケースでは悩みのタネになる時代遅れのセマンティクスにとらわれていた。

Life After Cats Effect

ZIOとCats Effectの型クラスの間に広がりゆく溝を埋めるために私は次のCats Effectのバージョンのデザインを買って出た。
この目的のため完全な型クラスの階層構造を描き出してフィードバックを求めた
その中では型付きのエラーとセマンティクスに関する主要な修正のみを含めた保守的な変更に絞った。

しかし私は最終的には奇妙で凝ったプレスリリースでTypelevelから締め出され、これは広く非難されている。
聞くところによるとこれはライブラリの理想的なデザインに関する'イライラさせる'ような討論によるものだそうだ。
その後、私の描いたスケッチは拒否されたが、多くの要素が後のCats Effect3へのproposalに含められている。

Cats EffectがZIOに追いつくことは無いだろうという認識が広まり、tagless-finalの大変な辛さもあいまってマーケットに地殻変動をもたらした。
すなわちZIO特化のライブラリをすぐに皆が作り出したのだ。

ライブラリ製作者の多くははじめに関数型プログラミングをZIOを通じて学んだ。
多くの場合彼らはどこかの中央機関の調整、計画、トレーニングなしにライブラリを作成した。
彼らはただ開発のためにZIOを選び、ライブラリやプロダクトを作ったに過ぎない。

時ともにZIOがそれ自身のエコシステムをもつであろうことがはっきりとしてきた。
それは様々な問題意識とアプローチを持ち、今までの関数型Scalaコミュニティの外側から人々が集まり、新しいグループを形成するに至った。

ZIO Today

今日ではZIOはモダンなアプリケーションを開発するための第一級の選択肢である。

モダンなアプリケーションは非同期かつ並行性を備え、リソースをリークしてはならず、デッドロックも引き起こさない、競合状態にも陥らず、容易にテスト可能であり、Scalaの型システムをバグを未然に防ぐために使用しなければならない。

ZIOによってパワフルかつ正しいアプリケーションをScalaの強みを生かして素早く開発することができる。

そう、ZIOは純粋関数型に結果としてはなった。そしてmonad transformerの代替手段だ。そしてそう、Cats Effectと協調してうまく動くとはいえ、次第にそれ自身のコミュニティとエコシステムとともに歩み始めている。

しかし関数型プログラミングと(今までの関数型プログラマには忌避されていたとしても)Scalaの機能を最大限活用してScala開発者を本当にハッピーで生産的にすることが最も重要なことである。

これらがなされていれば、他のことは些末なことだ。

info-outline

お知らせ

K.DEVは株式会社KDOTにより運営されています。記事の内容や会社でのITに関わる一般的なご相談に専門の社員がお答えしております。ぜひお気軽にご連絡ください。