1

Is it possible to define partially parameterize generic implicit class ? For instance assume I have following class

  implicit class IoExt[L, R](val io: IO[R]) {
    def wrapped(errorCode: String): IO[Either[ProcessingResult[L], R]] = ???
  }

How can I defined something like

type IoExtLocal[R] = IoExt[String, R]

And have IoExtLocal[R] be available as implicit class ?

The motivation is to free client code from specifying type parameter every time wrapped[](..) is called. It gets very verbose.

expert
  • 29,290
  • 30
  • 110
  • 214

2 Answers2

3

Just create another implicit class and import necessary one

  object ioExt {
    implicit class IoExt[L, R](val io: IO[R]) extends AnyVal {
      def wrapped(errorCode: String): IO[Either[ProcessingResult[L], R]] = ???
    }
  }

  object ioExtLocal { 
    implicit class IoExtLocal[R](val io: IO[R]) extends AnyVal {
      def wrapped(errorCode: String): IO[Either[ProcessingResult[String], R]] = 
        (io: ioExt.IoExt[String, R]).wrapped(errorCode)
    }
  }

  import ioExtLocal._

  trait SomeR
  val x: IO[SomeR] = ???
  x.wrapped(???)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thank you @dmytro-mitin ! In your solution every call of `wrapped` instantiates new object. Also compiler produces two wrapper classes per type. Could you propose solutions to these two shortcomings ? – expert May 12 '19 at 15:05
  • Thanks. I've reach same result as well. I'll accept your answer anyway. Could you please share your thoughts why Scala compiler doesn't produce value class call in example from my answer? – expert May 12 '19 at 16:13
1

After trying multiple solutions I've found that following works without instantiating helper class on every call to wrapped

trait IoExtTrait[L, R] extends Any {

  protected def io: IO[R]

  def wrapped(errorCode: String): IO[Either[ProcessingResult[L], R]] =
    io.attempt.map(_.leftMap(ex ⇒ FailureMsg[L](errorCode, Some(ex))))

  def wrappedT(errorCode: String): EitherT[IO, ProcessingResult[L], R] =
    EitherT(wrapped(errorCode))
}

implicit class IoExtLocalString[R](protected val io: IO[R]) extends AnyVal with IoExtTrait[String, R] {
  override def wrapped(errorCode: String) = super.wrapped(errorCode)
}

on the other hand following instantiates helper class on every call

implicit class IoExtLocalString[R](protected val io: IO[R]) extends AnyVal with IoExtTrait[String, R] {}

If anyone know why it happens please let me know. I'm on Scala 2.12.8 (same behavior with 2.13-RC1).

Further conversation at https://github.com/scala/bug/issues/11526 confirmed that allocation happens on both cases. Too bad.

expert
  • 29,290
  • 30
  • 110
  • 214
  • "Universal traits allow basic inheritance of methods for value classes, but they **incur the overhead of allocation**." https://docs.scala-lang.org/overviews/core/value-classes.html#introduction – Dmytro Mitin May 12 '19 at 16:32
  • @dmytro-mitin But how come adding `override` function which does nothing fixes this ? – expert May 12 '19 at 17:11