Scala2.11でのvarianceに関する変更とScalaz

まだ公式アナウンスはされてないけど、2.11.0-M8がmaven centralに上がってます。さて、Scalaz7.0.xの安定版*1は、Scala2.11.0でもクロスビルドする予定です。
*2
しかし、Scala2.11.0-M8でビルドしたら、大量にコンパイルエラーになりました。その話


で、このコンパイルエラーは完全に予期されていたことです。関連するissueなどのリンクは以下

https://github.com/scala/scala/pull/3184
https://issues.scala-lang.org/browse/SI-2066
https://github.com/scalaz/scalaz/issues/595
https://github.com/scalaz/scalaz/pull/328
https://github.com/scalaz/scalaz/pull/383

scala/scalaのpull reqのところで、scalazのコミッターの人たちが議論参加してます。また、Scalaz7.1.0の方の開発版のbranchでは、すでに対策済みで、( この件とは関係ないScala自体のバグ の1点を除いて)見事に2.11.0-M8でもコンパイル通りました。


Scala言語自体の変更というか、SI-2066のバグの内容は「型安全性壊すから、overrideの際のvarianceの制約を厳しくした」という感じだと思います。
Scala2.10(もっと細かく言うと、2.11.0-M7まで)は

def f[T[_]](x : T[Int])

をoverrideする際に

def f[T[+_]](x : T[Int])

に変更できたらしいですが、これが最悪ClassCastExceptionを発生させるらしいです。

というわけで、Scala言語自体の修正は正しいわけですが、Scalazの7.0.xのbranchでは、これを大量にやっている箇所があるみたいで、影響範囲が膨大です。

scalaz7.0.xは、specs2が依存していたりして、Scala2.11.0-M8のビルドをできるだけ早く出すのは、わりと重要なのです。ちょっと面倒だからといって、諦めるわけにはいかないです。(7.1.0のほうが安定版になっていないのもあるし)


というわけで、Scala2.11.0-M8でコンパイル通るように頑張って修正しないといけないんですが、どうなるんでしょう・・・。たぶん

  • 影響範囲少なくするには、無理やり型合わせるために一部内部でcast*3する・・・?
  • 素直に直したら、基本的に7.1.0のbranchと同じようにvariance使わない方向(非変、invariant)になる?

のどちらかでしょうか?


たぶん、どちらにしろ、Scalaz7.0.5以前と次に出すScalaz7.0.6でソースコード互換性はある程度崩れる気がします。しかし、JVMのtype erasureのお陰で、希望的観測ですが、バイナリ互換は保てるでしょう・・・たぶん。
7.0.xシリーズはバイナリ互換保つために、わざわざブランチ分けて開発してるので、もしバイナリ互換保てないことになったら一大事です・・・。

最悪

  • 7.0.xのScala2.10、2.9用branch
  • 7.0.xのScala2.11用branch
  • 7.1.xのbranch

と、3つに分けるという可能性もゼロではないですが、流石にそれはしんどいのでやりたくないですね・・・。
もしくは
「Scalaz7.1.0finalを、Scala2.11.0finalと同時に出す予定だったが、予定をはやめてだす。代わりに、7.0.xのScala2.11のビルドは諦める」
というシナリオ?


まぁこれらが心配しすぎで、(ちょっとソース互換崩れる程度で)無事バイナリ互換保ったまま7.0.xのScala2.11クロスビルドができるといいですね・・・。


結論がでたら、追記するか、別に書くと思います。



追記
2.11.0-M8でコンパイル通るようにvariance削除してpull reqした。やはりvarianceや型パラメータの数を変更しても、(mimaのpluginが正しいならば)バイナリ互換は崩れないらしい
https://github.com/scalaz/scalaz/pull/630


うっ、「特別にコンパイルオプションつけると、2.10の挙動を保つという機能追加したよ!」という流れになったぞ・・・ https://github.com/scala/scala/pull/3412 こういうの提供されないと思ったから、書き換えたのに(´・ω・`)まぁ自分の苦労は無駄になる(?)かもしれないけど、ユーザーにとっては、ソース互換性が崩れることなくScalaz7.0.xを使い続けられる可能性がでてきたので、いいニュースですな・・・。ただ、この機能が入るとしても、次のScala2.11.0-RC1がでるまで使えないので、結局Scalaz7.0.xのScala2.11.0-M8用はリリースどうするの?諦めるの?という問題は残るが



7.0.xのScala2.11.0-M8用は、いつくが案がでた(自分もだした)のですが、結局
「(おそらくほぼバイナリ互換だから)Scalaz7.0.5自体は、Scala2.11.0-M7でビルドしつつ、sbtでscalaBinaryVersionを2.11.0-M8に設定して "scalaz-core_2.11.0-M8" % "7.0.5" をリリースする」
https://github.com/scalaz/scalaz/commit/eb2db0016eedd24
という、裏ワザ的な解決策になりました。

*1:2013年4月に7.0.0が出て以来、バイナリ互換を保っているversion

*2:これ書いている時点での7.0.xの最新は7.0.5

*3:キャストというより、正確にはuncheckedVarianceアノテーションを大量につける