【Scala】Optionと非Optionを比較する時の話

2 min読了の目安(約1500字TECH技術記事

バージョン

Scala 2.13.1

今更感ありますが

val numOpt = Some(3)

val num3 = 3
val num4 = 4
val str = "str"

numOptと他の値を比較する際、

numOpt.contains(num3) // true
numOpt.contains(num4) // false

numOpt.contains(str) // false ※型が違うもの同士を比較しているのでそもそもコンパイルエラーで弾きたい

numOpt.contains(str)みたいなコードを書いてしまってバグらせてしまったことがありました。

なぜコンパイルエラーにならないのか

Option.containsについて見てみると、型パラメータA1については、AはA1のサブタイプであるという制約になっています。

https://www.scala-lang.org/api/2.13.3/scala/Option.html#containsA1%3E:A:Boolean

final def contains[A1 >: A](elem: A1):

型パラメータを特に指定していないのでコンパイラがA1をAnyだと推論しています。
なので今回の例の場合だと以下のようにすればエラーになってくれます。

numOpt.contains[Int](str)

エラー

type mismatch;
[error]  found   : String
[error]  required: Int
[error]     numOpt.contains[Int](str)

喰らえ! -Xlint:infer-any

とはいえ毎回型パラメータを指定するのも面倒です。Anyに推論されるのをやめさせたいですね。

XlintとはScalaのコンパイラscalacのオプションです。
build.sbtに設定しましょう

scalacOptions ++= Seq(
  "-Xlint:infer-any",
)

numOpt.contains(str)をコンパイルすると以下の警告を出してくれます。

a type was inferred to be `Any`; this may indicate a programming error.

エラーにしたい場合は-Xfatal-warningsも指定しよう。

scalacOptions ++= Seq(
  "-Xlint:infer-any",
  "-Xfatal-warnings",
)

Xlintについて

infer-anyだけではなく様々なlintingのオプションがあります(-Xlintとだけ指定すればそれらすべてをやってくれます。)
一覧は https://docs.scala-lang.org/overviews/compiler-options/index.html#Advanced_Settings

参考

-Xlint, -Xfatal-warnings, そして Scalafix を用いた Scala の厳格化 | eed3si9n