0

I have two implicit conversions that add an apply method.

implicit def f1(foo: Foo) = new {
  def apply(x: Int) = ???
}

implicit def f2(foo: Foo) = new {
  def apply(x: Int, y: Int) = ???
}

But I can't use them, because the compiler complains about ambiguous implicit conversions

foo(1) // compile error

Why does it complain if it is clear which one should be used?

2
  • You have implicit def f1(foo: Foo) and implicit def f2(foo: Foo) . So yeah, it is not clear at this point, even though the created objects would have apply()s with different signatures. Why not just put both apply()s into one implicit def? Commented Apr 24, 2017 at 10:07
  • @slouc Because one apply is from Predef. Commented Apr 24, 2017 at 10:55

2 Answers 2

3

You should include both apply()'s into one implicit:

implicit def f1(foo: Foo) = new {
  def apply(x: Int) = ???
  def apply(x: Int, y: Int) = ???
}

From http://docs.scala-lang.org/tutorials/tour/implicit-conversions:

An implicit conversion from type S to type T is defined by an implicit value which has function type S => T, or by an implicit method convertible to a value of that type.

So, you should have exactly one implicit method converting Foo to function.

How it works in your example case:

  1. Compiler sees foo(1) invocation.
  2. It replaces it with foo.apply(1).
  3. It founds, that class Foo doesn't have method apply and tries to find implicit conversion to class with this method.
  4. It founds two conversions, f1 and f2, and gives up on it.
Sign up to request clarification or add additional context in comments.

4 Comments

I can't merge those implicits, cause one of them is in Predef.
@Yaroslav I guess, you should update your question to be more specific then
@Yaroslav which version of Scala do you use and what is exactly the Foo class which you try to implicitly convert?
@Yaroslav it actually doesn't really matter, what your Foo class is, algorithm of disabling Predef imports is the same anyways. Check my another answer
1

If the problem lies in existing implicit inside Predef, you should disable Predef import like described here: Override Predef's implicit conversions

By example, let's try to make new apply function for String.

scala> implicit def stringToFunction(s: String) = new {
     |   def apply(x1: Int) = ???
     | }
stringToFunction: (s: String)AnyRef{def apply(x1: Int): Nothing}

scala> "123"(15)
<console>:13: error: type mismatch;
 found   : String("123")
 required: ?{def apply: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
 and method stringToFunction of type (s: String)AnyRef{def apply(x1: Int): Nothing}
 are possible conversion functions from String("123") to ?{def apply: ?}
       "123"(15)
       ^
<console>:13: error: String("123") does not take parameters
       "123"(15)
            ^

So, we should disable augmentString import from Predef:

scala> import Predef.{augmentString => _, _}
import Predef.{augmentString=>_, _}

scala> "123"(15)
<console>:14: error: type mismatch;
 found   : String("123")
 required: ?{def apply: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method wrapString in class LowPriorityImplicits of type (s: String)scala.collection.immutable.WrappedString
 and method stringToFunction of type (s: String)AnyRef{def apply(x1: Int): Nothing}
 are possible conversion functions from String("123") to ?{def apply: ?}
       "123"(15)
       ^
<console>:14: error: String("123") does not take parameters
       "123"(15)
            ^

Let's disable wrapString too, that will finally achieve what we wanted to do:

scala> import Predef.{augmentString => _, wrapString => _, _}
import Predef.{augmentString=>_, wrapString=>_, _}

scala> "123"(15)
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:284)
  at $anon$1.apply(<console>:12)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  ... 31 elided

You can do the same for implicit conversions from your class Foo, following compiler complaints about ambigious conversions.

1 Comment

Thank you very much, that works! And my apologies for being a subject of the XY problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.