1

If we define the following function:

def methodWithImplicit(explicit: String)(implicit imp: String) = {
  println(explicit + imp)
}

we can call it as follows:

methodWithImplicit("abc")("efg") //abc - explicit, efg - imp

And it works fine. Now consider the following TypeClass:

trait MyTypeClass[T] {
  def accept(t: T): T
}

which is going to be used inside extractor object:

object TestExtractor {
  def unapply(str: String)(implicit myTypeClass: MyTypeClass[String]): Option[String] =
    if (!str.isEmpty)
      Some(myTypeClass.accept(str))
    else
      None
}

So if we use it as follows:

implicit val myTypeClass:MyTypeClass[String] = new MyTypeClass[String] {
  override def accept(t: String): Unit = t
}

"123" match {
  case TestExtractor(str) => println(str)
}

It works ok. But how to pass the parameter explicitly when using with pattern matching? I tried

"123" match {
  case TestExtractor(str)(myTypeClass) => println(str) //compile error
}

and

"123" match {
  case TestExtractor(myTypeClass)(str) => println(str) //compile error
}

But it does not compile.

St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • 1
    Possible duplicate, probably related: https://stackoverflow.com/questions/12338469/implicit-parameters-wont-work-on-unapply-how-to-hide-ubiquitous-parameters-fro – Andrey Tyukin Mar 02 '18 at 17:51
  • @AndreyTyukin i just saw the bytecode of the pattern matching and noticed this. `// Method Main$.unapply:(Ljava/lang/String;LMyTypeClass;)Lscala/Option;`. It seems in pattern-matching and using extractor we have no chance to supply it explicit as it is desugared by the compiler itself. – St.Antario Mar 02 '18 at 18:02

1 Answers1

2

Since the left hand side seems to accept essentially nothing but trees built from stable identifiers, constant literals, and lower-case letters for variable names, I don't see any way to get closer to the desired syntax than this:

val `TestExtractor(myTypeClass)` = TestExtractor(myTypeClass)

"hello" match {
  case `TestExtractor(myTypeClass)`(str) => println(str)
}

This of course requires that you define the weirdly named value TestExtractor(myTypeClass) (in backticks) right before the match-case, so you can use it as a single symbol.

Full code:

trait MyTypeClass[T] {
  def accept(t: T): T
}

object TestExtractor { outer =>
  def unapply(str: String)(implicit myTypeClass: MyTypeClass[String]): Option[String] =
    if (!str.isEmpty)
      Some(myTypeClass.accept(str))
    else
      None

  class ExplicitTestExtractor(tc: MyTypeClass[String]) {
    def unapply(t: String) = outer.unapply(t)(tc)
  }

  def apply(tc: MyTypeClass[String]): ExplicitTestExtractor = 
    new ExplicitTestExtractor(tc)

}

implicit val myTypeClass:MyTypeClass[String] = new MyTypeClass[String] {
  override def accept(t: String): String = t.toUpperCase
}

val `TestExtractor(myTypeClass)` = TestExtractor(myTypeClass)

"hello" match {
  case `TestExtractor(myTypeClass)`(str) => println(str)
}
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93