scalaのmapValuesは、Mapのvalueだけにmapを適用したい時に使用するメソッドです(Mapとmapでちょっと分かりづらいですが)。
Scala 2.12.6で実行すると、以下のような結果になります。
scala> val characters = Map("Gandalf" -> "wizard", "Aragorn" -> "ranger") characters: scala.collection.immutable.Map[String,String] = Map(Gandalf -> wizard, Aragorn -> ranger) scala> val upper = characters.mapValues( x => x.toUpperCase ) upper: scala.collection.immutable.Map[String,String] = Map(Gandalf -> WIZARD, Aragorn -> RANGER)
まだ正式リリースされていないScala 2.13.0ですが、先日リリースされた2.13.0-M5という開発途中のバージョンで実行すると、以下のような結果になりました。
scala> val characters = Map("Gandalf" -> "wizard", "Aragorn" -> "ranger") characters: scala.collection.immutable.Map[String,String] = Map(Gandalf -> wizard, Aragorn -> ranger) scala> val upper = characters.mapValues( x => x.toUpperCase ) warning: there was one deprecation warning (since 2.13.0); for details, enable `:setting -deprecation' or `:replay -deprecation' upper: scala.collection.MapView[String,String] = <function1>
何か明らかに挙動が変わっています。
何が変わったのか?
ふとこの挙動の変化についてツイートしたところ、@xuwei_kさんに丁寧に教えていただきました。
Scala 2.12以前からその2つのメソッドだけ、評価が遅延される(しかしそれが型には現れてない、というわかりづらい状態だった)
— Kenji Yoshida (@xuwei_k) 2018年9月10日
↓
2.13では互換性の都合上、挙動はある程度維持しつつ型はViewにし、一旦非推奨にする
↓
2.14以降で他のメソッドとの一貫性の観点からもっと整理するかも、という予定(?)
遅延とは、mapValues呼び出した際に完全にすべてのvalueを評価せず
— Kenji Yoshida (@xuwei_k) 2018年9月10日
「もとのMapと、その渡したmapValuesの引数の関数、のペア」
を保持したMapを作って、getやapplyが呼ばれた場合だけ評価する、というものです(以下のコード実行するとわかる)
Map(1 -> 2,3 -> 4).mapValues{x => println(x);x}.get(1)
Scala 2.12.6における実装は以下の箇所の場所です(これも教えてもらった…ありがとうございます)。
mapValues
はMapを返すように定義されていますが、実態はMappedValues
という型のインスタンスを返しています。この時点ではインスタンスが作られるだけで実はmapが実行されていません。valuesを取り出すまで(get
が実行されるまで)mapValues
の引数に渡された変換関数は実行されていない、つまり遅延評価されるということです。
この挙動が型から分からない、ということが課題になった、ということですね。確かにscaladocにも書かれていないし、型は普通のMapだし、全然分からないですね。
Scala 2.13.0-M5での実装
MapView.MapValues
という型のインスタンスを返しています。
MapView
の実装は以下の通りになっています。
先ほどの結果にキーを渡すと、変換されたvalueが得られました。
scala> upper("Gandalf") res4: String = WIZARD
この変更はmapValues
だけでなく、filter
にも適用されています。filter
なんかはよく使われていると思うので、けっこう影響が大きいかもしれないですね。Scala 2.13.0へのアップデートの時には気をつけましょう。
Scala 2.13.0へアップグレードするとき
先ほどのmapValues
の件以外にも、網羅的にまとまっているマイグレーションガイドが有りますので、来たるべきScala 2.13.0リリースに向けて、こちらを見ておくと良いでしょう。