N W
N W

Reputation: 13

Issue with Scala Implicit Conversions

In the below code excerpt, the last statement does not compile. However, the statement immediately before that one does in fact compile. This second-to-last statement is what I would expect the compiler would convert the last statement to. I do not understand why it does not work. Any help is appreciated.

trait ParameterizedBy[A, B] {
    val parameterized: B
  }

object ParameterizedBy {
  implicit def toParameterized[A, B, C](p: ParameterizedBy[A, B])(
    implicit f: B => C): C = f(p.parameterized)
}

trait Wraps[A] {
  val wrapped: A
}

object Wraps {
  implicit def toWrapped[A](w: Wraps[A]): A = w.wrapped
}

val p = new ParameterizedBy[String, Wraps[Int]] {
  override val parameterized: Wraps[Int] = new Wraps[Int] {
    override val wrapped = 6
  }
}

ParameterizedBy.toParameterized(p)(Wraps.toWrapped) + 5

p + 5

Upvotes: 1

Views: 95

Answers (1)

Andrey Tyukin
Andrey Tyukin

Reputation: 44908

Implicitly converting a method into A => C is problematic, because the compiler cannot easily enumerate all the possible classes C that have the expected method +, and then go and search all the possible methods which take B and give C - this search would take forever.

I'd suggest to avoid implicit arguments of type B => C for unknown type C. If you want a converter, then give it some specific name, e.g.

trait Unwrap[A, B] extends (A => B)

that you then use only in this chained implicit.

A rough sketch of what you might try instead:

import scala.language.implicitConversions

trait ParameterizedBy[A, B] {
    val parameterized: B
  }

object ParameterizedBy {
  implicit def toParameterized[A, B, C](p: ParameterizedBy[A, B])(
    implicit f: Unwrap[B, C]): C = f(p.parameterized)
}

trait Wraps[A] {
  val wrapped: A
}

object Wraps {
  implicit def toWrapped[A](w: Wraps[A]): A = w.wrapped
}

trait Unwrap[A, B] extends (A => B)

object Unwrap {
  implicit def unwrap[A]: Unwrap[Wraps[A], A] = new Unwrap[Wraps[A], A] {
    def apply(w: Wraps[A]): A = w.wrapped
  }
}

val p = new ParameterizedBy[String, Wraps[Int]] {
  override val parameterized: Wraps[Int] = new Wraps[Int] {
    override val wrapped = 6
  }
}

p - 5         // works
(p: Int) + 5  // works with type ascription (to avoid conflicts with `+ String`)

Upvotes: 1

Related Questions