Implicit class sometimes cannot work

269 Views Asked by At

Scala version: 2.11.8

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import play.api.mvc._

object foooo {

  type ActionFilter[R] = R => Future[Either[Int, Unit]]

  case class FooRequest[A](request: Request[A]) extends WrappedRequest(request)

  implicit class ActionFilterOps[R](val f: ActionFilter[R]) {
    def * (f1: ActionFilter[R]): ActionFilter[R] = { (r: R) =>
      f(r).flatMap {
        case Left(r) => Future.successful(Left(r))
        case Right(_) => f1(r)
      }
    }
  }

  def test[A]() = {
    val f0: ActionFilter[FooRequest[A]] = { (r: FooRequest[A]) =>
      Future.successful(Left(1))
    }
    val f1 = f0
    ActionFilterOps(f1) * f0 // Ok
    f1 * f0 // Won't compile
  }


}

As you can see, implicit class not work. I have to new the ActionFilterOps

1

There are 1 best solutions below

0
On BEST ANSWER

As a side note: it helps if you have a self contained example that does not depend on external dependencies (such as Play) if that is possible.

Now onto the answer, or at least fix of the error... If you change the type alias ActionFilter to a class that wraps a function it works.

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

case class ActionFilter[R](f: R => Future[Either[Int, Unit]]) { def apply(r: R) = f(r) }
type Request[A] = Option[A]

case class FooRequest[A](request: Request[A])

implicit class ActionFilterOps[R](val f: ActionFilter[R]) {
  def * (f1: ActionFilter[R]): ActionFilter[R] = ActionFilter{ (r: R) =>
    f(r).flatMap {
      case Left(r) => Future.successful(Left(r))
      case Right(_) => f1(r)
    }
  }
}

def test[A]() = {
  val f0: ActionFilter[FooRequest[A]] = ActionFilter{ (r: FooRequest[A]) =>
    Future.successful(Left(1))
  }
  val f1 = f0
  ActionFilterOps(f1) * f0 // Ok
  f1 * f0 // Ok
}

It also works if you use the raw function type instead of the type alias.

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

type Request[A] = Option[A]

case class FooRequest[A](request: Request[A])

implicit class ActionFilterOps[R](val f: R => Future[Either[Int, Unit]]) {
  def * (f1: R => Future[Either[Int, Unit]]): R => Future[Either[Int, Unit]] = { (r: R) =>
    f(r).flatMap {
      case Left(r) => Future.successful(Left(r))
      case Right(_) => f1(r)
    }
  }
}

def test[A]() = {
  val f0: FooRequest[A] => Future[Either[Int, Unit]] = { (r: FooRequest[A]) =>
    Future.successful(Left(1))
  }
  val f1 = f0
  ActionFilterOps(f1) * f0 // Ok
  f1 * f0 // Ok
}

Especially that second "solution" makes me think that this is a bug.