Implicit parameters will not work if not applied. How to hide the ubiquitous parameters from extractors?

Apparently, unapply / unapplySeq in extractor objects does not support implicit parameters. Assuming here an interesting parameter a and an alarmingly ubiquitous parameter b, which would be nice to hide when extracting c.

[ EDIT ]: It seems that something was broken in my intellij / scala-plugin installation that caused this. I can not explain. I have had a lot of strange problems lately. After reinstalling, I can no longer repurpose my problem. Confirmed that unapply / unapplySeq allow you to use implicit parameters! Thank you for your help.

This does not work (** EDIT : yes, it is): **

trait A; trait C; trait B { def getC(a: A): C } def unapply(a:A)(implicit b:B):Option[C] = Option(b.getC(a)) 

In my understanding of what an ideal extractor should be, in which the intention is intuitive for Java people, this restriction basically prohibits extract objects that depend on additional parameters (s).

How do you usually deal with this limitation?

So far I have four possible solutions:

1) The simplest solution I want to improve: do not hide b, specify the parameter b together with a as a normal unapply parameter in the form of a tuple:

 object A1{ def unapply(a:(A,B)):Option[C] = Option(a._2.getC(a._1)) } 

in client code:

  val c1 = (a,b) match { case A1(c) => c1 } 

I don't like this because more noise is rejected that deconstructing a to c is important here. Also, since the Java people, who need to be sure that they really use this scala code, are faced with one additional syntax novelty (tuple bindings). They can get anti-scala aggression "What is all this? ... Why then not use the normal method in the first place and check if?".

2) extractors are defined inside the class encapsulating the dependence on a specific B, import extractors of this instance. The import site is a little unusual for java users, but the template matching site b hides well and it is intuitively obvious what is happening. My favorite. What was the flaw I missed?

 class BDependent(b:B){ object A2{ def unapply(a:A):Option[C] = Option(b.getC(a)) } } 

in client code:

 val bDeps = new BDependent(someB) import bDeps.A2 val a:A = ... val c2 = a match { case A2(c) => c } } 

3) declare extractor objects in the amount of client code. b is hidden since it can use "b" in the local area. Reuse of Hampers code is very dirty client code (in addition, it must be specified before using the code).

4) have an unapply return Function option B => C. This allows you to import and use a parameter-dependent, parameter-dependent, without providing b directly to the extractor, and instead the result when used. Java users can be confused by using function values, b are not hidden:

  object A4{ def unapply[A,C](a:A):Option[B => C] = Option((_:B).getC(a)) } 

then in client code:

  val b:B = ... val soonAC: B => C = a match { case A4(x) => x } val d = soonAC(b).getD ... 

Further comments:

  • As suggested in this answer , “view borders” can help get extractors with implicit conversions, but this does not help with implicit parameters. For some reason, I prefer not to do with implicit conversions.
  • considered "context boundaries," but they seem to have the same restriction, right?
+4
source share
2 answers

In what sense is your first line of code not working? Of course, there is no arbitrary prohibition of implicit parameter lists for extractor methods.

Consider the following setup (I use simple old classes instead of case classes to show that there’s no extra magic here):

 class A(val i: Int) class C(val x: String) class B(pre: String) { def getC(a: A) = new C(pre + aitoString) } 

Now we define the implicit value of B and create the extractor object using the unapply method:

 implicit val b = new B("prefix: ") object D { def unapply(a: A)(implicit b: B): Option[C] = Option(b getC a) } 

What we can use as follows:

 scala> val D(c) = new A(42) c: C = C@52394fb3 scala> cx res0: String = prefix: 42 

Just as you would expect. I do not understand why you need a workaround here.

+4
source

You have a problem in that implicit parameters are compilation time (static), whereas pattern matching is a dynamic approach.

 trait A; trait C; trait B { def getC(a: A): C } object Extractor { def unapply(a: A)(implicit b: B): Option[C] = Some(b.getC(a)) } // compiles (implicit is statically provided) def withImplicit(a: A)(implicit b: B) : Option[C] = a match { case Extractor(c) => Some(c) case _ => None } // does not compile def withoutImplicit(a: A) : Option[C] = a match { case Extractor(c) => Some(c) case _ => None } 

So this is a conceptual problem, and the decision depends on what you really want to achieve. If you want something next to an optional implicit, you can use the following:

 sealed trait FallbackNone { implicit object None extends Optional[Nothing] { def toOption = scala.None } } object Optional extends FallbackNone { implicit def some[A](implicit a: A) = Some(a) final case class Some[A](a: A) extends Optional[A] { def toOption = scala.Some(a) } } sealed trait Optional[+A] { def toOption: Option[A]} 

Then where you have implicit b: B , you will have implicit b: Optional[B] :

 object Extractor { def unapply(a:A)(implicit b: Optional[B]):Option[C] = b.toOption.map(_.getC(a)) } def test(a: A)(implicit b: Optional[B]) : Option[C] = a match { case Extractor(c) => Some(c) case _ => None } 

And the following compilations:

 test(new A {}) // None { implicit object BImpl extends B { def getC(a: A) = new C {} } test(new A {}) // Some(...) } 
+1
source

Source: https://habr.com/ru/post/1433215/


All Articles