In Scala, the interaction of overloading and implicit argument resolution seem to make it impossible to make the following code usable.
trait Bijection[A, B] extends Function1[A, B] with Unapply[A, B] { self =>
def apply(a: A): B
def unapply(b: B): A
}
sealed trait Unapply[A, B] {
def unapply(b: B): A
}
object Bijection {
implicit def biject[A](a: A): Biject[A] = new Biject(a)
implicit object IntStringBijection extends Bijection[Int, String] {
override def apply(a: Int): String = a.toString
override def unapply(b: String): Int = b.toInt
}
}
sealed class Biject[A](a: A) {
def as[B](implicit f: Function1[A, B]): B = f(a)
def as[B](implicit f: Unapply[B, A]): B = f unapply a
}
The goal here is for a.as[B] to perform a typesafe conversion irrespective of whether a Bijection[A,B] or a Bijection[B,A] is available in implicit scope.
The reason that this doesn't work is that implicit resolution appears to take place after overload disambiguation in the compiler, and since both implementations of 'as' have the same result type, the compiler doesn't even get around to attempting to find out whether an开发者_开发问答 appropriate implicit is in scope that can perform the conversion. In short, implicit resolution is not used in overload disambiguation.
The reason I want to have 'as' overloaded is to avoid the need for the user of this library to need to encode the "direction" of the bijection at the call site; obviously one could implement Biject as this:
sealed class Biject[A](a: A) {
def viaForward[B](implicit f: Function1[A, B]): B = f(a)
def viaReverse[B](implicit f: Unapply[B, A]): B = f unapply a
}
but this is really unappealing because it essentially makes the pimp superfluous; one might as well explicitly pass the bijection, but then of course you lose the ability to have the bijection that is used vary based upon the scope.
Is there any good solution to this problem?
How's this?
trait Bijection[A, B] extends Function1[A, B] with Unapply[A, B] {
self =>
def apply(a: A): B
def unapply(b: B): A
}
sealed trait Unapply[A, B] {
def unapply(b: B): A
}
object Bijection {
implicit def biject[A](a: A): Biject[A] = new Biject(a)
implicit object IntStringBijection extends Bijection[Int, String] {
override def apply(a: Int): String = a.toString
override def unapply(b: String): Int = b.toInt
}
}
sealed class Biject[A](a: A) {
def as[B](implicit f: Either[Bijection[A, B], Bijection[B, A]]): B = f.fold(_ apply a, _ unapply a)
}
trait EitherLow {
implicit def left[A, B](implicit a: A): Either[A, B] = Left(a)
}
object Either extends EitherLow {
implicit def right[A, B](implicit b: B): Either[A, B] = Right(b)
}
import Bijection._
import Either._
1.as[String]
"1".as[Int]
精彩评论