( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

implicit conversionを定義する際に最低限チェックすべきこと

軽くイラッ☆っとしたので書く。


Scalaにはimplicit conversionってのがあってまぁ知らないならググれ。で、implicit conversionを定義する際に、最低限チェックすべきたったひとつのことを書く。


scala.Predefでimplicit conversionが定義されている型に対して新しいimplicit conversionを定義する際は、Predefで変換される型と、新しく定義する変換先の型とでメソッド名が重複しないかチェックしろ!!」


具体例で話す。scala.PredefにはStringからStringOpsへのimplicit conversion(もう長くてウゼェので以下ICな)が定義されている。StringOpsってのは文字列操作に関するわくわく便利メソッドが詰まってるtraitだ。

  implicit def augmentString (x: String): StringOps


で、 StringOpsにはlinesってメソッドがある。Stringから行のIteratorを作ってくれる非常によく使うアレな。

scala> val str = Seq("aaa","bbb","ccc").mkString("\n")
str: String = 
aaa
bbb
ccc

scala> val strops = Predef.augmentString(str)
strops: scala.collection.immutable.StringOps = 
aaa
bbb
ccc

scala> strops.lines
lines                 linesIterator         linesWithSeparators   

scala> strops.lines
res7: Iterator[String] = non-empty iterator

scala> str.lines
res8: Iterator[String] = non-empty iterator


augmentStringでStringOpsへのICがあるから、いきなりstr.linesとか読んでも大丈夫になってる。ここまではまぁおさらい。


では、ここでおもむろに便利すぎて汁漏らしたと評判のscala.sys.processパッケージをimportしてみる。


Scala 2.9.0のscala.sys.processパッケージが便利過ぎる件について - Scalaとか構文解析についてあれこれ書く日記

scala> import scala.sys.process._
import scala.sys.process._

scala> str.lines
<console>:12: error: type mismatch;
 found   : str.type (with underlying type String)
 required: ?{val lines: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
 and method stringToProcess in trait ProcessImplicits of type (command: String)scala.sys.process.ProcessBuilder
 are possible conversion functions from str.type to ?{val lines: ?}
       str.lines
       ^

えっ?さっきまでちゃんと使えていたStringOps#linesが使えないよ?なぜなんだぜ?


これは、エラーメッセージにもあるように、scala.sys.process.ProcessImplicitsによってStringから ProcessBuilderへのICが定義されたからだ。

implicit def stringToProcess (command: String): ProcessBuilder


scala.sys.process.ProcessBuilderには、StringOpsと同じlinesって名前のメソッドが定義されている。だから、単にStringに対してlinesを呼び出した場合は、コンパイラがaugmentStringかstringToProcessかどちらのICを適用すべきか判断できないのでコンパイルエラーになってるって訳だよファック!!氏ね!!


これを回避するためには、以下のように型アノテーションを付けてメソッド呼び出ししてやれば回避できる。が、本来はライブラリ作成者が注意すればいいだけの話だ。

scala> (str:StringOps).lines
res14: Iterator[String] = non-empty iterator

このようなICでのメソッド名重複による影響は、救いがたいことにScalaSDK内でも結構ある。加えて、specs2などの著名なライブラリでもよくかち合ってしまう。クソが。


今のところ、このような重複をユーザー側のコードで解決させるには型アノテーションしかない。将来のScalaで、ICに関するもう少し細やかな制御(優先度付けや、スコープ内のICの無効化)などができるようになるといいねファッキン!!


ぎゃぴーっはぁっきゃっきゅい!!いやぁ…ほにゃっ、ぅうぅ!ぅ…きゅわーー!!!いっああ!!!ぎゃう!ぎょーっきゅんんはにゃっぱぎょーー!!っきゅい!!!!!!ぬい!!ぃぱ!モフモフモフモフ!!らぬわふにゃう!!!!!!!!!!!おるすぁん!きゅわふぇぁっきゅわぁっはーーっ…ああ!!うふぇぇ、わーっほぉおおるぁぁあひぎょーっあっ!ごるぁ…ふーー!っ、ぎゃっきゅんんどるぷ?ぎょぱー!モフモフ!おぺぺぺ?ぅ…あ…くんんはにょーっ!んくんっ!ぬぇ!はにゅいやぁんどるぁー!へぺっぁぁんっほにゅわふ、おるぽっきゃーっー!あ、らぬぇ、んはにょぱー?ぎぃい!!!!ぬぃぱ…きゃぴー!ぎぃっ!うぅ!!!!!ぃぱ!ごぶぁんはにゅわぁ…い!!!ふふぬぇー!ぬわぁー!きぇ!うっあぁんんきゃーっーー、きゅわふぬふ!ぎゃう!!!!!う!!!!!!にょーっほにゅわぁあふふぬふぁぁあひぃ?ごるぽっう!よぅぅ!!!!!っぁぁーーー、へぺぺぺっ!!!!きゃっぱぁぁー?ふぅ…くんどるぷぇぁっほにゃー!ふぁーーーっぁん!!!!ぬるぽっぱー!っううふぇー!い!!へぺっう!はー!!ああぁ…よぅ!ごるぷ?うわーーー!へぷ。モフ!!!はぁー!!!きぇぁ…あ…ん。へぷぇぇぁぁっほぉおるぁあっほにょぱー!!らぬぇぇ!っほぉおぺっー?モフモフモフ!!!らめぇぇぁー!!!はにょぱぁん!にゃっぱぎょぱぁああっぱ!きゃー!ぃいっ…きゃぴーっうっほにゅんきぇー。ふにゃーーーー!へぬい!あぃぃっうわふぁーっほぉおぺ!ん?っぱーっ!んんんくんくんん!ごぶぁーー。ぎぃ…っきゅわふぇぁんっきゃーっほぉおるぽっほぉおん!ぬわふ!ひぎゃあ、ぃいっ!!!い!!っほにょー!!!ひゃっほにゅわーーっはにゅいっぱぁんっ…あぁっ…あぃぱぁっ…くんくんくんきぇー!ぬふふふぅぅうふぅ!!!!ぅう!っはにょー?う!!うっ!はぁん。ほぉおんんっ!ほにゅんはぁ…あんどるぷぇぁんきゅんんどるすぁんっきゃぴーー!ひぎょぱぁーーーー!ふーっぁ……きぇ!ぬるすぁぁあ!ぬわぁぁっはー、へぺ?ふぁぁ…ああふぬふぬふ!らめぇー!ひぎゃあ…きゅいやぁんくんはにゅんきゅわー!はにゅい!モフ!!もるすぁあ、あひゃぴーっぁっううっ…あんん!!ひぃぱぁ…あ……あひぃぃぃぱ?ほぉおぺ、ごぶぁあ!!よぅ!わふぬふ!っぁ…あふぅ……んきゅいっほぉおおんっぁあ…あんっあふぬふー。わふーっあふー?いっあ!ぎゃう!…あぁ…あ…らぬぃぱぎょー!!!!モフモフモフ!ぎぃ…あんはーっぱ?よぅぅ…んくんどるぁあ…おおぺっあふにゅわぁー!!はーー、へぺっ?ん、へぬいっはー、らめぇ。にょぱーーっうっ!ほにょぱぁーーー?おるぷ、っぁっー。…んはぁ?っうふー!わふぬぃ?わふーーっあひゃあんきゃあ!いっあんどるぷ…うわー!きぇぇぇぁーーー!!ぬいやっうぅう!!ぎゃっきぇ!ひゃぴー!へぬわふぬるぁ…へぷぇぁんくんはーっ!ぬぃいやっー!!!!ぅ!ぬぃっあ!!!ぃぃぃっああん!わーっあ…くんどるすぁーーー!ふぁっー!っ!!にょーーっーーっああぁ…う!い!いっ…んきゃううぅ…ん。きゃっううっほぉおおんはにょーっうぅう!にゃあふぁあぁあんはぁ…きゃうわーー、…きゅわふにゃぴーっ…もるぁ。んはーっーっほにゅわふ、ふぇ?ほにょぱー!!!らぬぇーっぁ…あ…もるぷぇ?ううぅ!あ!!!!!おおるぽっ!へぬい!!!!!モフモフモフ!!きゅんはにょーーっ…くんっ!にょー、わぁんんきぇぇぁぁっ…くん!わぁ!ぅううわー、っーー!!モフモフ!!い!!ぃぱぎゃーー!らめぇぁーーーーー!!ほぉおるすぁぁ…きゃーっうぅ!へぬるすぁ。……くん!わふーっはぁあひゃぴーー!!へぷぇー…ぬるぷ。おぺぺぺ!わー!!あっ!scala> println(g) ぬいっ!わー!!!モフモフ!!おるぽっうふふぇぇぇぇ。モフモフ!!ぃぱーーっ!!んはにゃぴー。あふ?にゃぴーーー!うわふ?わふぇ。っほぉおおぺぺ…わー!モフ!お!っー…っぁー!ひゃー!いやぁぁ…あんはにゃあひぎゃーーーーー!あぃいっきぇー。ぬるすぁ…んきゃっうわぁーっぁあぃぱぁーっきぇぁんどるぷぇぁっ!!!ほにゅん。はにょーっあんっきゅんはーーー!ぅ…きゅい!んきぇー!!ぅ…んはぁー!!!!ぬるぷぇぁっうぅ…きゃー、ほにゃー!ぅ!おんはにょぱぎゃっー?へぺぺぺっぱぎょーっあひぎぃいやぁー!モフモフモフ!!ぬるすぁ………あんはぁぁん…はぁん…ふふ。ごぶぁあぃっ!んんどるぽっ。モフ!ぃ、にょぱぎゃう!!!!あっ…くん!ぬわぁぁ…きゃあんはにゃあふふふふにゅいっほにゅわふふー!!きぇ?っぁ。わふ!よぅう!モフ!らめぇーー…もるぽっ…くん!!!あっぱ、あふぇー!はにゅいっ。っ…くんはーー、ぎぃっうふぁ?っはぁ!ん。もるすぁあふぁ、ぅうっきゃうっぱぎぃぱーっ?いやぁん…へぷ?はぁっはーー!おぺ?うわー。ぎぃ!にゃうぅ…きぇぇぇぇ?よぅううう!!うふぁあん!!!!!よぅぅ!はーっ……くん!ぎゃあぁあひぎょぱぁーー?よぅううっ!!!んんっほぉお!ごるぁあひぃぃいやっう!もるぽっー!ごぶぁあ…くんくんきゅん。よぅううふぅ…くんんんはぁ…おぺっ!うふぬるすぁっきゃぴー?ほにょぱぎぃいっあ!!!!ひゃあぃぱぁぁんくんんはぁーーー?へぺ!もるぁあ…あっ!ぬいやっー?ぅうふぇ、はぁ、はにゅい!ぬぇ。ぎぃぱー!いやっぁ、ふぁ?あひゃーーっ!はぁああっーーっぁ。ほぉおるぽっー!ぅうぅうぅ!!いやっ…ん!っーー!んきぇ、ぎゃあ、ほにょぱーー?ふにゅわー、っぱ!はー!ほにゃうぅ……んんきゃっ…あ!!ひぎゃぴー!ぅ…あ!んどるぷぇぇ。ひぎょーーっぁ…くんどるすぁんくん!っあひゃー!!!!ああぁんどるぷぇ、ごるすぁー!!!!おぺぺぺ…ごぶぁん!にょぱぁあぁぁぁぁ…んっー!よぅう!!よぅぅぅ…ぬぇぇー。きゅんきゅんっ……いっあ?もるぁ…ああ!よぅぅぅ……きゅんきぇぁあ!ん!きぇぁあひぃいっ!ぎゃっうわふ…ぃ?ぬふぁぁ…んんどるぷ。はぁっうわふ?……くんん!らぬいやっうふぬわぁ、あ。…ぃっああ…あんんんはー!きぇぇ。ほぉおぺぺぺっきぇぁんきゃう!きぇぁぁ。っほぉお!!おぺ?へぺ、ぃぃ!うう!!!おぺっ…あふぬぃっあひぎょぱーっきゃっ、…あっあ!!らぬぇー、おぺ…あ?らめぇ、うっー!!!ぃぃっーっほにゃうわふにょーっあ……きゃうわぁぁっぱぎゃっぁん!にゅわーっぁ…わふぬぃぃっぁあんくん…モフ!!ぃぱー!ふぬわぁんどるぽっ…くんくんはぁぁんんはーーー?ぅ…くんどるぷ?らぬいやぁ…きゅん?にょぱぎゃぴーっ…はぁーっ!モフ!にゅんきゅいっぱ!うわふふ。ぬぃ!いやぁ……きゅわふぅぅ…あ…くん!!おぺぺぺっ…あっ!い!きぇーー!モフモフ!!!…もるすぁあぃいっうう!!!うわふにょーー、ぃ…ああふぬぇぁ…んきゃうううう!!!おんはーーー、よぅ…わぁあひゃっ…んはにょーーっきゃー。らめぇ!っ?ぬぃ。よぅ…あ!ぎぃぱ!よぅ…にゅいやぁっう!!!!!!ふふぇ!んはぁー!!はにゃぴー!!!はー!!ひぃいやっ。モフ!!!!!よぅ…きゅんくんどるぷ!いっあふ?よぅ…んっ…くんっあぃい!らめぇぁぁーーーー。あぁ!へぬぇ。ごぶぁーっ…ひぃっほぉお!もるすぁー!ぃっぱぎょぱぁあ…ん!…よぅぅぅ…あぁ…きぇ…はにょぱ?よぅ!…くんくん!…んきゅいっうぅぅううっー、ぎぃ!ぎょーっー!!