1
votes

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 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)
}
1
Could you post a reduced, self-contained code sample that shows the error message you're getting? I don't quite understand your intent.Kipton Barros
Also: In the answer to the Stack Overflow question you linked, retronym states: Please consider this as a opportunity to learn about erasure, context bounds and implicit search, rather than as a pattern to be applied in real code! Maybe there's a simpler way to achieve what you need.Kipton Barros
I updated the article to make my intentions and conclusions clearer.Tim Friske

1 Answers

1
votes

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).