【问题标题】:How can I get around this limitation of overload resolution in Scala?如何解决 Scala 中重载分辨率的限制?
【发布时间】:2010-10-03 17:39:18
【问题描述】:

在 Scala 中,重载和隐式参数解析的相互作用似乎使以下代码无法使用。

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
}

这里的目标是让 a.as[B] 执行类型安全转换,而不管 Bijection[A,B] 或 Bijection[B,A] 在隐式范围内是否可用。

这不起作用的原因是隐式解析似乎发生在编译器中的重载消歧之后,并且由于 'as' 的两个实现具有相同的结果类型,编译器甚至没有尝试找出适当的隐式是否在可以执行转换的范围内。简而言之,在重载消歧中不使用隐式解析。

我想让'as'重载的原因是为了避免这个库的用户需要在调用站点对双射的“方向”进行编码;显然可以这样实现 Biject:

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
}

但这真的没有吸引力,因为它本质上使皮条客变得多余;不妨明确地通过双射,但当然你失去了使所使用的双射根据范围而变化的能力。

这个问题有什么好的解决办法吗?

【问题讨论】:

    标签: scala overloading implicit


    【解决方案1】:

    怎么样?

    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]
    

    【讨论】:

    • 这太棒了,反义词,谢谢!我曾想到,Either 可能需要扮演一个角色,但我从未想过将双射本身投射到 Either 空间中。如果我正确理解这一点,那么它之所以有效,正是我最初的尝试没有成功的原因:在将参数隐式解析为“as”之前,对 Biject 的初始隐式转换是“完成”的,这使得左右要应用的隐式转换。
    • 关键是使用隐式优先级来避免1.as[Int] 情况下的歧义。
    • 看起来即使没有低优先级的隐式技巧,这也有效。 left() 和 right() 都可以在 Bijection 对象中声明,(并且它们可以被专门化,以便它们仅适用于 Bijection 实例)并且它仍然有效。
    • 这是一个加强了隐式转换的版本:paste.pocoo.org/show/270683
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-09
    • 2016-04-21
    • 2014-08-30
    • 2019-12-16
    相关资源
    最近更新 更多