Scala 2の標準ライブラリには、大昔から以下のようなimplicitlyという、初心者にとっては一見謎なメソッドが存在します。
def implicitly[T](implicit e: T): T = e
これの話。
Scala 2の標準ライブラリはScala 3でも共通なので、implicitlyはScala 3からも使用可能です。
なんでこんなものがあるのか?というと、以下の説明がある程度関係しますが
implicitというものを型クラスとして考えるなら?型クラスとして捉えなくても?基本的に implicit なもの(Scala 3だとgivenなもの)は、定義時も参照時も、名前指定で参照するべきではない、けど、スコープにあるものを取りたい、という場合に型だけを指定して取得するためのメソッドです。
さて、Scala 3には implicitly と一見ほぼ同じだけれど、微妙に違う summon というメソッドがあります。
transparent inline def summon[T](using x: T): x.type = x
inline なので、実行時にはメソッド呼び出しにならず、compile時に埋め込まれます。
また transparent もついて x.type になっているので、以下のように型も違いが出ます。
Scala 3でのimplicitly、特別扱いされずに元の定義のまま扱われるっぽいので、transparent inlineなsummonとは明らかに挙動変わるじゃん pic.twitter.com/orhGO9Kqiy
— Kenji Yoshida (@xuwei_k) February 4, 2024
わざわざ微妙に別のものを用意したのは
- それらの細かい違いをあえてつけたかった
- 理想的なScala 3だけの世界においては
implicitという言葉は撲滅させたいのでimplicitlyという名前は嫌だったので、ほぼ同じだが別名で用意した
の2通りが考えられます。どちらの理由が強くて作ったのか?は歴史を調べてないので知りません。そこの詳細は今回の主題ではないので割愛。
さて、型が変わる挙動の違いについては、大抵問題にならないというか、どちらでもいいとはいえ、細かい違いがあるの、両方使い分けるの???みたいな気持ちになって悩ましいですね。
また、JVMの実行時の最適化が効けばおそらく違いは出ませんが implicitly の方は、(最適化のオプション指定しない限り?)メソッド呼び出しが残る、(普通はprimitiveをそのままimplicitにしないと思うがprimitiveの場合)boxingに違いが出るかも?、といった細かい違いもあります。
さて、それはそれとして
transparent inline って普通のメソッド呼び出しよりもcompile時に少し特殊な処理が必要だから、若干compile速度に影響が出るのでは???
という直感があり、以下で多少真面目にベンチマークを取ってみました。
Scalaは3.6.4です。その他詳細な条件は実際の以下のコード見てください。
それぞれを100万箇所も呼び出して、10回くらい繰り返しcompileをしてみた結果が以下です。 単位は秒
summon
94 79 75 78 77 76 77 76 79 76
implicitly
62 48 44 44 44 45 43 47 44 45
JVMの温まり考慮して雑に初回だけ除くとして
- summon: 平均77.0秒。中央値77秒
- implicitly: 平均44.9秒。中央値44秒
となります。summonの方がcompile速度が1.7倍くらい遅いです。
悲しいことに予想が当たってしまった・・・。
とはいえ、数十倍以上の差が出るならともかく、1.7倍程度なら許容範囲というか気にする必要はほぼないですかねぇ・・・???
もう少し別のパターンというか計測方法で実験したら、多少違う結果が出るかもしれませんが。