少し前に、こっそりと、かなりひさしぶりな(互換壊す)メジャーリリース?となる、7.3.0 finalをリリースしたけど全然リリースノート書いたり宣伝もしていないScalazですが、 もう最近自分しか開発していない感があり、猫のほうが明らかに流行っています。
が、それはそれとして、なにがあろうと、ゆったりとScalazの開発はやれる限り続けようと思うので、"そろそろいけるかな?"と思い、ScalazでのDotty対応をやってみたら(ごく一部を除いて)できたので、その知見をまとめました。
kind-projectorがとにかく辛かったけど、マクロは使っていないので、そういう意味ではまぁなんとかなりました・・・(?)
このblog書いてる時点のDotty 0.24.0-RC1時点の情報です。 Dotty 0.24.0-RC1でビルドしたscalazは7.4.0-M1としてpublish済です。 7.3や7.2にbackportしてのDotty対応は、クロスビルドしつつの互換考えると結構厳しいし、Dottyの安定度もわからない(7.2や7.3では、まだあまりDottyの仕様変更に振り回されたくない)ので、現状では、やるかどうかわかりません。
まだDottyのコンパイラは割と簡単にクラッシュしたりするので、Dottyがproduction readyになるのは結構時間かかりそうだなぁ、と思いました。
(nightlyビルド使ってもクラッシュする)
(tweetもしたけど、あるいはおそらく無限にthreadが終わらなくなる)
以前scalapropsに関して書いたものも合わせてどうぞ
- kind-projectorの機能をフルに使っていた部分がとにかく辛い(数千行書き換え)
- 一部は使えるので
-Ykind-projector
指定 https://github.com/scalaz/scalaz/commit/17543261bb37e5af8344a55516d54ebe8cc85936 - 結構前からだが
?
は非推奨で*
になったらしいので置換(?
はワイルドカードになるらしい?) https://github.com/scalaz/scalaz/commit/17c1b9e2f2e4bcd6aeb8360d3769b05ace07b769 Lambda
とλ
両方使えたけど、現状の0.24.0-RC1のdottyではλ
しか使えないので書き換え- infix typeでは使えないので、例えば
Functor[Int \/ *]
をFunctor[\/[Int, *]]
に書き直す必要があった - higher kindの場合やvarianceやupper, lower boundがあるのも無理(古いtype lambdaに書き直し)
- poly lambdaという機能もない(普通の無名クラスに書き直し)
- 一部は使えるので
- インデントベースのシンタックスが導入されたことにより、(括弧があるScala 2と互換ある書き方でインデントに頼ってなかったとしても)インデントが変だと警告出るので修正
- https://github.com/scalaz/scalaz/commit/50e085273d93f9e6d40ce3dd389afaf3935a7a2f
- https://github.com/scalaz/scalaz/commit/ab960ab4c6e8ce5e7d30ea31b680d352c0696dab
- https://github.com/scalaz/scalaz/commit/e77f57f08c09983ce7195066290a0ac6d21d107b
- https://github.com/scalaz/scalaz/commit/1dcd3c5586b87b3a1fd60828c839d5240fc57bb8
- インデントベースのシンタックスが導入されたことにより、tabとspace混ざってると怒られるのでtab消した https://github.com/scalaz/scalaz/commit/872bc223f82c9697e2b6497de6762416cfe6f9ff
- implicit valやimplicit defの戻り値、その他メソッド呼び出し時の型パラメータなど、怒られる(怒られなかったところもあるかもしれないけど試行錯誤のついでに)様々なところに、とにかく型を明示するか、違う方法で呼び出すなどした
- https://github.com/scalaz/scalaz/commit/b43174a5bc94c424c73d89d0131c9c73f18602d7
- https://github.com/scalaz/scalaz/commit/9fb00d3dc28afee42d19e076ed2cbd4aa29ba07f
- https://github.com/scalaz/scalaz/commit/322439d78de4235eafcd4bbeeef2ca6fc8dcae62
- https://github.com/scalaz/scalaz/commit/bbc7c8678c47f0f6c9f436b894470c12e077277c
- https://github.com/scalaz/scalaz/commit/d9be6cf0a261c5a7aac03e6f490c03dab2632782
- https://github.com/scalaz/scalaz/commit/45ec3197db959794b0c4642889c8fb1f133a8b3b
- https://github.com/scalaz/scalaz/commit/d599ea862b50d867fa859afa60163b62f9820ef0
- https://github.com/scalaz/scalaz/commit/425241c75bd0ff183d3e2c4babaeca21517de1e9
- https://github.com/scalaz/scalaz/commit/0317e4cbe9e01ef8a3b0fcfe7e691c80a5c36ca4
- https://github.com/scalaz/scalaz/commit/b29aacda99540b638228a7b550bf2f8bc1e8f941
- https://github.com/scalaz/scalaz/commit/bf4710dca45842b0833d83806353dc21b4ee2c2c
Array()
はNothingのClassTag見つからない、で怒られる
- forSome使えないので、別の手段で可能なところは別の手段に書き換え、代替方法が思いつかずに無理なところは一旦諦めた
- Freeの内部実装のやつは、NaturalTransformationで代用可能なのでそうした https://github.com/scalaz/scalaz/commit/0a97bc6a23c3438b9312916dade4b70357d0e749
- Forallはdottyならpoly function入るので必要ないだろうから諦めて消す。MonadErrorのOpsで使われてるやつもわからんので一旦諦め https://github.com/scalaz/scalaz/commit/ed5fea131c6a9ee8e7f6280aec71ae05766bffa7
- メソッドを
def foo()
みたいに()
付与して定義したところは、呼び出し側も()
付与しないといけないので付与(引数ゼロのcase classでも)- https://github.com/scalaz/scalaz/commit/9df6df7882a1d55c6527e4d161e556dca4989b3e
- https://github.com/scalaz/scalaz/commit/1b968c006f00f5c5c7eab77dcf7f84e6f0c0820a
- https://github.com/scalaz/scalaz/commit/35d70acfff6b63913c4478c505899fbfc97ea4dd
- https://github.com/scalaz/scalaz/commit/6836fc3f980f62031a2dfd0e4d3e180b8376384a
- https://github.com/scalaz/scalaz/commit/614ea01fdac6cafe1993e5fa09bdc57526696046 (これは逆に定義側含めて消した)
- scalazでの記号がdottyの予約語?予約記号?キーワード?と衝突したのでクオートするか、諦めて消す
- https://github.com/scalaz/scalaz/commit/56b5cbb39ba82ae1a3641bbadda419b17171a219
=>>
使えない(type lambdaの矢印になるらしい https://github.com/lampepfl/dotty/commit/88567f1d458c1aa380681cfd1d61de594c24e8d8 ) - https://github.com/scalaz/scalaz/commit/ffa0f3f0da53535857a737f48931b66560c8a77d
=>>
使えない - https://github.com/scalaz/scalaz/commit/7c8effb8264d4eda6867cf9c9777b59831c0411b
?=>
使えない(context functionとかいうやつになる?)
- https://github.com/scalaz/scalaz/commit/56b5cbb39ba82ae1a3641bbadda419b17171a219
- 必要ないobjectのfinal消す(これは2.13でもコンパイラオプションによっては警告出るか・・・?) https://github.com/scalaz/scalaz/commit/d1b1a973d174067be2cb02ccd1d22139ac5df380
- 型パラメータの名前に
+
使えない(varianceの記号だから)と言われたので名前変更 https://github.com/scalaz/scalaz/commit/856f073da2b781fea8aa2e7ff06213a075519fb6 - enumは予約語になったので、quoteしたり名前変更
- 型パラメータの名前に
_
は使えない?と言われたので名前変更 https://github.com/scalaz/scalaz/commit/e5a631f8ca72b294b31264af99603ff67a01d5dc - case classのunapplyが(少なくとも現状のdottyでは)Optionを返さなくなってそのままではScala 2とcross buildできないので使うのやめた https://github.com/scalaz/scalaz/commit/847143682fe05373faada2ff68c378753b41a569
- まだ使えるけど、なんか将来消えそうな雰囲気も感じるのでpostfixOpsやめる
- 無名関数で引数1つでも
{ a: Int => a + 2 }
みたいに型を書いた場合は{ (a: Int) => a + 2 }
みたいに括弧で括るか、(型省略可能な場合は)省略するか、どちらかをしないと怒られるので、どちらかの修正した- https://github.com/scalaz/scalaz/commit/4788e2c787d7fb0e9030b47899236c48f474bc7b
- https://github.com/scalaz/scalaz/commit/9e47a4f7b21ecb193d9e78e513c08344f059aed3
- https://github.com/scalaz/scalaz/commit/17641854ce8691359294a0f301dd2d57d6c55161
- https://github.com/scalaz/scalaz/commit/31358497905210b17688736a05ed36ad2ec42722 逆に型消したパターン
- https://github.com/scalaz/scalaz/commit/723f46a31735622a90b26934e5efd345ae523ac8 これも消したパターン
- Function.uncurriedでby-nameの場合型が合わない、と怒られるので、使うのやめる https://github.com/scalaz/scalaz/commit/59f20aa5b695106b9daeac6c88c0b877d230ddd1
- オーバーロードだかオーバーライドと認識されるのかよくわからんけど怒られたのでStateTの内部実装をrename https://github.com/scalaz/scalaz/commit/8a121c20665a1cd1fba094acb91a9d2472b868f0
- Liskovのとあるメソッドのimplicitが衝突すると言われたので、優先順位つけるために移動 https://github.com/scalaz/scalaz/commit/c65027c7ac7fafa86c7341badaebd2a7049d4de8
private val value
な変数で(implicit ev: value.type <:< なんとか
みたいなメソッドがあると、Scala2では怒られないのに、dottyだとprivateがpublicに漏れ出してるからダメ、みたいに言われるので変更 https://github.com/scalaz/scalaz/commit/3ad5468c6a4de42f414feb6e2f3a892c50cdf29c- 定義はできるけど、呼び出し時に曖昧だと怒られるのでIsoFunctorTemplateというやつのメソッド名変更 https://github.com/scalaz/scalaz/commit/e085e3ffd97accdd88a5ff18e23c68b6a3e66a0a
- カン拡張のインスタンスの実装で、型合わないと言われるので書き方変えた https://github.com/scalaz/scalaz/commit/07f09ebeebbed6c477b09f8b3518049a075e14f0
- dottyのdoc生成がバグってて?エラーになるのでひとまず生成しない https://github.com/scalaz/scalaz/commit/7a55bada436d31e7c407577dadba4c6a7123f78e
- scalaz.Unapplyの呼び出し側が基本的にダメなのでテストしない https://github.com/scalaz/scalaz/commit/ed5fea131c6a9ee8e7f6280aec71ae05766bffa7 (scalaz.UnapplyのテストのせいでDottyのコンパイラ死ぬ気がする)
- UnapplyProductというやつの実装で
U1#A
みたいな型を参照してるが、Dottyでは、この機能無くなったので諦め https://github.com/scalaz/scalaz/commit/ed5fea131c6a9ee8e7f6280aec71ae05766bffa7 https://dotty.epfl.ch/docs/reference/dropped-features/type-projection.html - implicit conversionが曖昧で衝突すると言われた https://github.com/scalaz/scalaz/commit/03f93f39b6f942db49f1d1d5099e4c17ea1d0d1e
- ImmutableArrayの一部が、varianceで警告出るようになってるけど、まだ未対応(コンパイルオプションでScala2Compatと指定してるから、ギリギリまだエラーにはならずに警告で許してくれるらしい?)
その他、移行の途中で気がついた点や感想
twitter.comhttps://t.co/8dQ0KCGOSf
— Kenji Yoshida (@xuwei_k) 2020年5月18日
dottyだとAnyに推論されるように変わってるな
twitter.comhttps://t.co/jyXqiGBZ7A
— Kenji Yoshida (@xuwei_k) 2020年5月16日
ここDottyだと通らないのだけど、Scala 2だと通るのは逆にScala 2のbugだろうか。定義が
NaturalTransformation[-F[_], +G[_]]
となってるならわかるけど
↑ これは諦めた
twitter.comDotty、特定のパターンで無限にコンパイル(の一部のスレッド?)が終わらなくなる気がする、まだ色々厳しい。
— Kenji Yoshida (@xuwei_k) 2020年5月14日
Scala 2はCtrl + Cで、(sbtは死なずにcompilerだけ)ちゃんと終わるように色々工夫していたはずだけど、それもそんなにまだされてなさそうというか
twitter.comhttps://t.co/H5RP195rkQ
— Kenji Yoshida (@xuwei_k) 2020年5月13日
dotty、implicitのimport候補教えてくれる機能搭載されてるけれど、これ全探索するとしたら、compile速度が爆遅になる要因になりそうな気がするけれど、大丈夫なのか・・・
(timeoutとかも実装されてるが)
twitter.comdottyがimplicitを優先順位順かなにかでsortしようとしてjava.util.TimSortに渡したところ、Comparatorに整合性がなくて
— Kenji Yoshida (@xuwei_k) 2020年5月13日
"Comparison method violates its general contract"
という例外で死ぬ。
エラーになるコードだからなのか?何もわかってないし、シンプルな例も作れてないので、issue報告は後で
↑このblog書いてる時点でまだ報告できてない(scalaz.Unapply使うとたぶん発生する)