EDIT: Added in information about F[_]
Here's the setup. We have a parent class which contains a type, and a method which processes a type projection of this, across all parent instances:
class Parent[F[_]] {
// These depend on information from Parent (ie F) so
// cannot be moved outside
trait Inner { def execute[A]: F[A] }
case class Foo(i: Int) extends Inner { ... }
case class Bar(s: String) extends Inner { ... }
def process(value: Parent#Inner): Unit = value match {
case Foo(_) => println("integer")
case Bar(_) => println("string")
}
}
The problem is that inside the case match, references to Foo
and Bar
are this.Inner
, rather than Parent#Inner
. So the following fails:
val foo = (new Parent[IO]).Foo(5)
val processer = new Parent[IO]
processer.process(foo) // match error
One way around this is to change def process
to:
def process(value: Parent#Inner): Unit = value.asInstanceOf[this.Inner] match {
case Foo(_) => println("integer")
case Bar(_) => println("string")
}
(note the new .asInstanceOf
).
However, this is unsatisfying.
Beyond extracting def process
out to a third-party class somewhere, is there a nicer way to achieve our desired behaviour?
EDIT:
The classes unfortunately need to be defined inside Parent
due to the dependence on the F[_]
. We could in theory move them outside as the initial answer suggests but this would introduce too much work and variety elsewhere since we would need to parameterise each Inner
subclass by an F[_]
EDIT 2:
One potential solution is to reformulate process
like this:
def process(value: Parent#Inner): Unit = value match {
case _: Parent[F]#Foo => println("integer")
case _: Parent[F]#Bar => println("string")
}
But this means we cannot use Foo
s unapply method. The following is invalid:
case Parent[F]#Foo(_) => println("integer")
In the case where Foo
is instead for example Foo[A, B, C](a: A, b: B, c: C)
this would mean the match statement becomes:
case _: Parent[F]#Foo[A, B, C] @unchecked => ...
which introduces a lot more complexity and potential for failure into the pattern match.
You can use type projections in the pattern match:
Another approach which does let you use
unapply
:You can also use
value.parent.Foo(_)
as a pattern if you makeInner#parent
aval
.