4

Define

type TA[T] = T => Int

implicit class TAOps[T](a: TA[T]) {
  def foo(): Unit = {println("TA")}
}

val ta: TA[Double] = x => x.toInt

Now,

ta.foo()

fails to compile with the message value foo is not a member of ammonite.$sess.cmd1.TA[Double],

while the explicit call

TAOps(ta).foo()

prints TA. Why does the implicit conversion not work in the former case?

Kamil Kloch
  • 333
  • 1
  • 9

1 Answers1

2

Your implicit def is expecting a type that receives one type parameter, i.e. a TA[T]

Your declaration: val ta: TA[Double] = ... is a type it self, and doesn't take any type parameters. So the compiler will not use your implicit def to type check this.

Conclusion you have an implicit type conversion for a type that takes a type parameter and TA[Double] doesn't take any type parameters.

Solutions:

1 - Replace the implicit type conversion to receive a Function1:

  implicit class TAOps[T](a: T => Int) {
    def foo: Unit = {
      println("TA")
    }
  }

2 - Use type lambdas:

  implicit class TAOps[T](a: ({type Alias = TA[T]})#Alias) {
    def foo: Unit = {
      println("TA")
    }
  }

Here the type you created is curried. So the compiler will now apply this implicit conversions to types that match, it is no longer expecting a type that receives 1 type parameter.

More on Type Lambdas

pedromss
  • 2,443
  • 18
  • 24
  • Thank you, @pedromss, it works. I wasn't aware that the type parameters 'holes' play role in this scenario. – Kamil Kloch Aug 24 '17 at 08:56
  • I too found out the hard way :P. Does this answer your question? – pedromss Aug 24 '17 at 09:04
  • This explanation doesn't sound quite right. The type of `a` in `TAOps` is not a type expecting another type. It is `TA[T]` which is fully applied. If this was the case then it would fail for `trait TA[T] extends (T => Int)` too but it doesn't. The solutions are appropriate but there must be more to the reasoning why this doesn't work. – steinybot Aug 04 '23 at 09:43