I tried to combine implicit arguments with case classes, but I got stuck.
case class C(i: Int)(implicit b: Boolean)
val c1 = C(1)(true)
implicit val b = true
val c2 = C(2)
c1 match {
case C(i)(b) => // doesn´t work
case C(i,b) => // doesn´t work
case C(i) => // works, but wanted: if (b) i else 0
}
According to the Scala Language Specification it´s due to the compiler generated extractor object for case classes: My implicit Boolean
is not a member of the resulting case class, so it has to be in the second (implicit) argument list (that I can´t find in the companion object´s apply method, unfortunately):
A case class definition of
c[tps](ps1 ). . .(psn)
with type parameterstps
and value parametersps
implicitly generates an extractor object (§8.1.8) which is defined as follows:
object c {
def apply[tps](ps1 ). . .(psn): c[tps] = new c[Ts](xs1 ). . .(xsn)
def unapply[tps](x: c[tps]) =
if (x eq null) scala.None
else scala.Some(x.xs11, . . . , x.xs1k)
}
How can I define a case class with members that are implicitly supplied at creation time?
Case classes are Scala's way to allow pattern matching on objects without requiring a large amount of boilerplate. In the common case, all you need to do is add a single case keyword to each class that you want to be pattern matchable.
Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.
Pattern matching is a way of checking the given sequence of tokens for the presence of the specific pattern. It is the most widely used feature in Scala. It is a technique for checking a value against a pattern. It is similar to the switch statement of Java and C.
Scala case classes are just regular classes which are immutable by default and decomposable through pattern matching. It uses equal method to compare instance structurally. It does not use new keyword to instantiate object. All the parameters listed in the case class are public and immutable by default.
You can define a case class with implicit arguments, but as you've discovered, they're not available for pattern matching. You could always write your own extractor though:
case class C(i: Int)(implicit val b: Boolean)
// You can't call this C because that seat is taken (can't overload the default unapply)
object C_ {
// In order to be able to select `b` here,
// it needs to be declared as "implicit *val* b: Boolean"
def unapply(in: C) = Some((in.i, in.b))
}
c1 match {
case C_(i, b) => ...
}
Alex's answer is clever, however I don't really like the _
in the object name, I find the syntax a little strange and remembering the underscore makes pattern matching more difficult to use. (Of course this is all subjective so your feelings may vary).
My first approach to dealing with this were to move the implicit parameters to the apply
method in the companion object.
case class A(i: Int, b: Boolean)
object Foo {
def apply(i: Int)(implicit b: Boolean): Foo = apply(a, b)
}
But this results in
Error:(21, 14) double definition:
method apply:(i: Int, b: Boolean)com.workday.cloud.master.package.A and
method apply:(i: Int)(implicit b: Boolean)com.workday.cloud.master.package.A at line 24
have same type after erasure: (i: Int, b: Boolean)com.workday.cloud.master.package.A
case class A(i: Int, b: Boolean)
^
We can fix this by adding an extra unused implicit parameter.
object A {
def apply(i: Int)(implicit b: Boolean, unused: DummyImplicit): A = apply(i, b)
}
Which approach you choose is up to you, I find this approach enables my client code to look more natural.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With