开发者

How can I get around this limitation of overload resolution in Scala?

开发者 https://www.devze.com 2023-01-18 18:49 出处:网络
In Scala, the interaction of overloading and implicit argument resolution seem to make it impossible to make the following code usable.

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]
0

精彩评论

暂无评论...
验证码 换一张
取 消