Scala
scalatest

`org.scalatest.Matchers._`と`org.scalactic.Tolerance._`を同時にimportすると、`+-`の呼び出しでエラー発生

環境

  • Scala 2.12.5
    • sbt 1.1.3
    • ScalaTest 3.0.5

やりたいこと

Scalaのテストコードで、浮動小数点数の比較をしたいです。
ScalacticのToleranceトレイトで定義されている+-メソッドを、使います。
http://www.scalactic.org/user_guide/Tolerance

問題

以下のテストコードを実行したら、+- is not a memberというエラーが発生しました。

SampleTest.scala
import org.scalatest.FreeSpec
import org.scalatest.Matchers._
import org.scalactic.Tolerance._

class SampleTest extends FreeSpec {

    "tolerance test" in {
      2.00001 shouldBe (2.0 +- 0.01)
    }
}
sbt_console
>testOnly SampleTest

[error] SampleTest.scala:12:29: value +- is not a member of Double
[error]       2.00001 shouldBe (2.0 +- 0.01)
[error]                             ^
[error] one error found
[error] (Test / compileIncremental) Compilation failed

解決

import org.scalactic.Tolerance._文を削除したら、エラーは発生しませんでした。

原因

ライブラリのソースを参照

SampleTestがimportしているMatchersは、Toleranceトレイトをミックスインしています。

org.scalactic.Matchers.scala
trait Matchers extends Assertions with Tolerance with ShouldVerb with MatcherWords with Explicitly { matchers =>

}
org.scalactic.Tolerance.scala
trait Tolerance {

  final class PlusOrMinusWrapper[T: Numeric](pivot: T) {

    def +-(tolerance: T): Spread[T] = {
    //...
    }
  }

  import scala.language.implicitConversions

  implicit def convertNumericToPlusOrMinusWrapper[T : Numeric](pivot: T): PlusOrMinusWrapper[T] = new PlusOrMinusWrapper(pivot)
}

Scala Compiler Option-Xlog-implicits

scalacのオプションには、「implicitが適用されない理由」を教えてくれる、-Xlog-implicitsというオプションがあります。

-Xlog-implicit-conversions
Print a message whenever an implicit conversion is inserted.

https://docs.scala-lang.org/overviews/compiler-options/index.html

build.sbtにscalacのオプションを設定して、テストを実行しました。

build.sbt
scalacOptions ++= Seq("-Xlog-implicits")
scalaVersion := "2.12.5"
sbt_console
>testOnly SampleTest

[info] SampleTest.scala:12:25: <convertNumericToPlusOrMinusWrapper: error> is not a valid implicit value for Double(2.0) => ?{def +-: ?} because:
[info] reference to convertNumericToPlusOrMinusWrapper is ambiguous;
[info] it is imported twice in the same scope by
[info] import org.scalactic.Tolerance._
[info] and import org.scalatest.Matchers._
[info]       2.00001 shouldBe (2.0 +- 0.01)
[info]                         ^
[error] SampleTest.scala:12:29: value +- is not a member of Double
[error]       2.00001 shouldBe (2.0 +- 0.01)
[error]                             ^
[error] one error found

implicitに関する情報が出力されました。
内容は、以下の通りです。

  • convertNumericToPlusOrMinusWrapperへの参照が曖昧
  • org.scalactic.Tolerance._org.scalatest.Matchers._で、同じスコープ内で二重にimportされている