Reputation: 5844
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?
Upvotes: 20
Views: 13308
Reputation: 10928
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.
Upvotes: 6
Reputation: 7979
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) => ...
}
Upvotes: 24