Last active
October 16, 2017 12:51
-
-
Save loicknuchel/a08a7efedc0e9877f1b53f4ba0112b20 to your computer and use it in GitHub Desktop.
How implicit class resolution works ?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.scalatest.{FunSpec, Matchers} | |
object Implicits { | |
implicit class SeqEitherExtension[E, A](val elt: Seq[Either[E, A]]) extends AnyVal { | |
def partition: (Seq[E], Seq[A]) = { | |
val (left, right) = elt.partition(_.isLeft) | |
(left.collect { case Left(e) => e }, right.collect { case Right(a) => a }) | |
} | |
} | |
} | |
class ImplicitsSpec extends FunSpec with Matchers { | |
describe("SeqEitherExtension") { | |
import Implicits._ | |
it("partition") { | |
// works | |
SeqEitherExtension(Seq(Left("err"), Right(1))).partition shouldBe(Seq("err"), Seq(1)) | |
// do not work, implicit not found :( | |
// Any idea on why implicit class is not found ??? | |
Seq(Left("err"), Right(1)).partition shouldBe(Seq("err"), Seq(1)) | |
/** | |
* Error:(24, 34) missing argument list for method partition in trait TraversableLike | |
* Unapplied methods are only converted to functions when a function type is expected. | |
* You can make this conversion explicit by writing `partition _` or `partition(_)` instead of `partition`. | |
* Seq(Left("err"), Right(1)).partition shouldBe(Seq("err"), Seq(1)) | |
*/ | |
} | |
} | |
} |
First of all the issue can be reduced to pure scala no library involved :
object Implicits {
implicit class SeqEitherExtension[E, A](val elt: Seq[Either[E, A]]) extends AnyVal {
def partition: (Seq[E], Seq[A]) = {
val (left, right) = elt.partition(_.isLeft)
(left.collect { case Left(e) => e }, right.collect { case Right(a) => a })
}
}
private val e: Seq[Either[String, Int]] = Seq(Left("err"), Right(1))
val expli: (Seq[String], Seq[Int]) = SeqEitherExtension(e).partition
val impli: (Seq[String], Seq[Int]) = e.partition
}
It looks like a bug to me since according to scala language specification :
- In a selection e.m(args) with e of type T, if the selector m denotes some member(s) of T, but none of these members is applicable to the arguments args. In this case a view v is searched which is applicable to e and whose result contains a method m which is applicable to args. The search proceeds as in the case of implicit parameters, where the implicit scope is the one of T. If such a view is found, the selection e.m is converted to v(e).m(args).
In the reported case e: Seq[Either[String, Int]]
does define a partition
method but the argument list is not compatible. I would also expect the compiler to look for a view which defines a member with a compatible argument list before throwing a compilation error for an unapplied method ...
Sounds worth a post on users https://users.scala-lang.org/ to get more enlightened feedback before a bug report :D
PS:
shouldBe(Seq("err"), Seq(1))
should be shouldBe((Seq("err"), Seq(1)))
you are missing parens on the tuple
from your comment it sounds like the parameterless case is processed under
- In a selection e.m with e of type T, if the selector mm does not denote an accessible member of T. In this case, a view v is searched which is applicable to e and whose result contains a member named m. The search proceeds as in the case of implicit parameters, where the implicit scope is the one of TT. If such a view is found, the selection e.m is converted to v(e).m.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For the record, adding parentesis to the partition method will remove the ambiguity between method call and function reference