1

Here is one idiomatic scala 2 example:

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
}

As you see, it has already been partly converted to the usage of path dependent types. But how to create an implicit value of Mapper for an O type which references self.Content (such as O =:= self.Content itself)?. Ideally a solution which would have straightforward equivalence between Scala 2 and Scala 3.

Turin
  • 2,208
  • 15
  • 23
  • 1
    Oh, sorry: a path dependent type. I'll edit the question to make it clear. – Turin Oct 05 '20 at 17:56
  • 1
    Why do you call this magnet pattern? I guess a magnet (on contrary to a type class) is defined with implicit conversions. I can't see implicit conversions here (`BoxExtension` just defines extension method). `Mapper` is just a type class. – Dmytro Mitin Oct 05 '20 at 18:27
  • I guess the previous (deleted) [version](https://stackoverflow.com/questions/64211627/how-to-translate-the-use-of-type-projections-to-pdt-in-implicits) ([copy](https://gist.github.com/DmytroMitin/4f27259403e509b35c7b080c08bfe509)) of your question was helpful to undestand the context of what you're asking . – Dmytro Mitin Oct 05 '20 at 18:43
  • Yeah, sorry. I found that the minimization didn't work as expected: the implicit `map`/`flatMap` was not picked up, because of type inference deficiences with type projections: `Mapper[B, B#Content]` was not picked up as a value for `Mapper[Box { type Content = Int }, Int]`. So I deleted it to rethink the whole thing again. – Turin Oct 05 '20 at 21:08
  • And you are right about the magnet pattern of course. I conflated in my mind a couple of real life problems that lead to this question. – Turin Oct 05 '20 at 21:32

1 Answers1

3

You can make an implicit def for this. Since Dotty doesn't allow type projections on abstract types, you'll need an extra type parameter. Also, I had to make self public because it was used in the signature of 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
}

Full example

I would normally suggest using type parameters for Box and (and an extra one for Mapper) instead, but it gets a little complicated. Perhaps you could turn BoxExtension into something like this, with C as an extra parameter:

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)
}

If you're open to using just Dotty and not cross-compiling, you can do this

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))
  }
}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
user
  • 7,435
  • 3
  • 14
  • 44
  • Still processing this to see if the solution after de-minimization still works, but there is one issue here: I am 95% positive that the `BoxExtension` implicit conversion will not work in Scala 2 due to type inference failure - same case as `[C <: Iterable[T], T]` requiring explicit type argument list. There is a workaround for that, but I have no idea if it will work in Scala 3 because it relies on undocummented behaviour of the type inferer. Or is not a problem at all in Dotty? – Turin Oct 05 '20 at 21:12
  • @Turin Hmm, oddly enough, I am [getting](https://scastie.scala-lang.org/U1MvFlDkR5aQEqATvx2bWA) an AbstractMethodError. Looks like a bug in the compiler. You're right about it not being inferred, though, let me try to fix that – user Oct 05 '20 at 21:56
  • @Turin This [compiles](https://scastie.scala-lang.org/rIz4RFtMQmyk3hfB5QbWLg), although I still can't figure out the AbstractMethodError. I thought turning off worksheet mode might fix it, but it didn't – user Oct 05 '20 at 22:25