Why does DummyImplicit not disambiguate [String](a: A) from (a: String)
Given the following piece of code:
final case class Attr[A](name: String)(implicit conv: String To A) {
def apply(value: A)(implicit dummy: DummyImplicit) = Attribute(name, value)
def apply(value: String) = Attribute[A](name, value)
}
The Scala compiler complains with "ambiguous reference to overloaded definition" when it sees the values hereafter:
1| val FooAttr = Attr[String]("foo")
2| val catch22 = FooAttr("bar")
Line 1: My intent is it to pass to the factory "Attr" producing "Attributes" the value type "String" as well as the name "foo" for all the attributes it ever produces.
Line 2: Using the previously configured attribute factory I am actually producing an attribute named "foo" with the value "bar" of type "String".
My conclusion: Because the parameterized type "A" for this factory object is "String" the Scala compiler deduces the same parameter signatures of method "apply" being "(value: String)" which are ambiguous. Therefore I tried to make a difference in signature by adding an implicit parameter list.
After having read an article开发者_StackOverflow中文版 about type erasure and DummyImplicit and consulting the Scala reference section "7.2 Implicit parameters" I thought "(implicit dummy: DummyImplicit)" would do the trick.
For the moment my solution is to have the minimal wrapper:
final case class Txt(str: String) {
override def toString = str
}
Given that an implicit value of type "Str To Txt", i.e. a suitable conversion function, could be found, the second line from above compiles, i.e.:
2| val catch22 = FooAttr("bar")
It seems I was thinking too complicated. Instead to overload the apply
method with a parameter list of (value: String)
, I simply got rid of it. The version that lives up to my full expectations now looks like:
final case class Attr[A](name: String)(implicit conv: String To A) {
def apply(value: A) = Attribute(name, value)
}
How about this?
case class Attribute[A](name: String, value: A)
case class AttrBuilder[A](name: String)(implicit conv: String => A) {
def apply[B](value: B)(implicit conv: B => A) = Attribute(name, conv(value))
}
val bldr = new AttrBuilder[String]("MyAttrs")
bldr("hello") // Attribute(MyAttrs, hello)
implicit def int2string(x: Int) = x.toString
bldr(2) // Attribute(MyAttrs, 2), using int2string implicit
bldr(false) // fails; cannot find implicit Boolean => String
The attribute builder bldr
will take any value of type B
that's convertable to A == String
, including strings themselves (the implicit conversion Predef.conforms
is used in that case).
精彩评论