最近奇妙な問題に直面するまで、私は Scala の暗黙の記述を理解していると思っていました。
私のアプリケーションにはいくつかのドメインクラスがあります
case class Foo(baz: String)
case class Bar(baz: String)
そして、文字列からドメイン オブジェクトを構築できるクラス。実際のデシリアライズを実行するためにサブクラス化することもできますが、それは問題ではありません。
class Reads[A] {
def read(s: String): A = throw new Exception("not implemented")
}
次に、暗黙のデシリアライザがあります
implicit val fooReads = new Reads[Foo]
implicit val barReads = new Reads[Bar]
文字列をドメインクラスの1つに変換するヘルパー
def convert[A](s: String)(implicit reads: Reads[A]): A = reads.read(s)
残念ながら、それを使用しようとすると
def f(s: String): Foo = convert(s)
次のようなコンパイラエラーが発生します
error: ambiguous implicit values:
both value fooReads of type => Reads[Foo]
and value barReads of type => Reads[Bar]
match expected type Reads[A]
def f(s: String): Foo = convert(s)
^
私にとって、コードはシンプルで正しいように思えます。Reads[Foo]
そして、Reads[Bar]
完全に異なる型ですが、何が曖昧なのでしょうか?
実際のコードははるかに複雑ですが、play.api.libs.json
この簡略化されたバージョンでもエラーを再現するには十分です。
ベストアンサー1
あなたの例で遭遇している曖昧さは、Scalacにどちらを使用したいかを伝えていないことです。コードを次のように置き換える必要があります。
def f(s: String): Foo = convert[Foo](s)
どちらを使用するかを判断するためです。 の戻り値の型から推測することはできませんf
。ここでは明示的にする必要があります。
コメントへの返信
ここで、あえて言わせていただきます。
trait Foo
case class Bar(s: String) extends Foo
case class Baz(s: String) extends Foo
def f(s: String): Foo = convert(s)
Bar
との両方に 1 つずつ定義されていると仮定すると、どの暗黙の が使用されるのでしょうBaz
か。他にも厄介なコーナー ケースはたくさんあると思いますが、このケースが私には目立ちます。