1

这是一个惯用的 scala 2 示例:

trait Box {
    type Content
    val content :Content
}

implicit class BoxExtension[B <: Box](private val self :B) extends AnyVal {
    def map[O](f :self.Content => O)(implicit mapper :Mapper[B, O]) :mapper.Res =
        mapper(self)(f)
}


trait Mapper[B <: Box, O] {
    type Res
    def apply(box :B)(f :box.Content => O) :Res
}

如您所见,它已经部分转换为使用路径相关类型。但是如何为引用Mapper的类型(例如自身)创建隐式值?理想情况下,这是一个在 Scala 2 和 Scala 3 之间具有直接等价性的解决方案。Oself.ContentO =:= self.Content

4

1 回答 1

3

您可以为此创建一个隐式定义。由于 Dotty 不允许对抽象类型进行类型投影,因此您需要一个额外的类型参数。另外,我不得不self公开,因为它被用于map.

object Mapper {
  implicit def uselessMapper[B <: Box{type Content = C}, C]: Mapper[B, C] { type Res = AllPurposeBox[C] } =
    new Mapper[B, C] {
      type Res = AllPurposeBox[C]
      def apply(box: B)(f: box.Content => C) =
        new AllPurposeBox(f(box.content))
    }
}

class AllPurposeBox[T](override val content: T) extends Box {
  type Content = T
}

完整示例

我通常建议将类型参数用于Boxand (以及一个额外的用于Mapper),但它会变得有点复杂。也许你可以变成这样BoxExtension的东西,作为一个额外的参数:C

implicit class BoxExtension[B <: Box {type Content = C}, C](private val self: B) extends AnyVal {
  def map[O](f: C => O)(implicit mapper: Mapper[B, O]): mapper.Res =
    mapper(self)(f)
}

如果你愿意只使用 Dotty 而不是交叉编译,你可以这样

trait Mapper[B <: Box, O] {
  type Res
  def apply(box: B)(f: box.Content => O): Res
  extension (self: B) def map(f: self.Content => O): Res = apply(self)(f)
}
object Mapper {
  given uselessMapper[B <: Box{type Content = C}, C] as Mapper[B, C] {
    type Res = AllPurposeBox[C]
    def apply(box: B)(f: box.Content => C) = new AllPurposeBox(f(box.content))
  }
}
于 2020-10-05T18:14:32.730 回答