编辑:添加了有关F[_]
这是设置。我们有一个包含一个类型的父类,以及一个在所有父实例上处理该类型的类型投影的方法:
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")
}
}
问题在于,在大小写匹配中,对Foo
和Bar
的引用是this.Inner
,而不是Parent#Inner
。因此,以下操作将失败:
val foo = (new Parent[IO]).Foo(5)
val processer = new Parent[IO]
processer.process(foo) // match error
一种解决方法是将def process
更改为:
def process(value: Parent#Inner): Unit = value.asInstanceOf[this.Inner] match {
case Foo(_) => println("integer")
case Bar(_) => println("string")
}
(请注意新的.asInstanceOf
)。
但是,这并不令人满意。
除了将def process
提取到某个地方的第三方类之外,还有没有更好的方法来实现我们期望的行为?
编辑:
不幸的是,由于对Parent
的依赖,需要在F[_]
内部定义这些类。从理论上讲,我们可以按照初始答案的建议将它们移出外部,但这会在其他地方引入过多的工作和多样性,因为我们需要通过Inner
F[_]
子类
编辑2:
一种可能的解决方案是像这样重新编写process
:
def process(value: Parent#Inner): Unit = value match {
case _: Parent[F]#Foo => println("integer")
case _: Parent[F]#Bar => println("string")
}
但这意味着我们不能使用Foo
的未应用方法。以下是无效的:
case Parent[F]#Foo(_) => println("integer")
如果使用Foo
例如Foo[A, B, C](a: A, b: B, c: C)
,则这意味着match语句变为:
case _: Parent[F]#Foo[A, B, C] @unchecked => ...
这会在模式匹配中引入更多的复杂性和失败的可能性。
答案 0 :(得分:2)
您应该使用依赖于路径的类型编写
class Parent[F[_]] {
trait Inner {
def execute[A]: F[A]
}
case class Foo(i: Int) extends Inner {
override def execute[A]: F[A] = ???
}
case class Bar(s: String) extends Inner {
override def execute[A]: F[A] = ???
}
def process(value: Inner): Unit = value match {
case Foo(_) => println("integer")
case Bar(_) => println("string")
}
}
val processer = new Parent[IO]
val foo: processer.Inner = processer.Foo(5)
processer.process(foo)
或带有类型投影
class Parent[F[_]] {
trait Inner {
def execute[A]: F[A]
}
case class Foo(i: Int) extends Inner {
override def execute[A]: F[A] = ???
}
case class Bar(s: String) extends Inner {
override def execute[A]: F[A] = ???
}
def process(value: Parent[F]#Inner): Unit = value match {
case _: Parent[F]#Foo => println("integer")
case _: Parent[F]#Bar => println("string")
}
}
val foo: Parent[IO]#Inner = new Parent[IO].Foo(5)
val processer = new Parent[IO]
processer.process(foo)
参数化Foo
且未检查类型匹配的示例:
class Parent[F[_]] {
trait Inner {
def execute[A]: F[A]
}
case class Foo[B](i: Int) extends Inner {
override def execute[A]: F[A] = ???
}
case class Bar(s: String) extends Inner {
override def execute[A]: F[A] = ???
}
def process(value: Parent[F]#Inner): Unit = value match {
case _: Parent[F]#Foo[_] => println("integer")
case _: Parent[F]#Bar => println("string")
}
}
答案 1 :(得分:1)
您可以在模式匹配中使用类型投影:
def process(value: Parent[F]#Inner): Unit = value match {
case _: Parent[F]#Foo => println("integer")
case _: Parent[F]#Bar => println("string")
}
另一种允许您使用unapply
的方法:
// Start writing your ScalaFiddle code here
class Parent[F[_]] { self =>
// These depend on information from Parent (ie F) so
// cannot be moved outside
trait Inner { def parent = self }
case class Foo(i: Int) extends Inner
case class Bar(s: String) extends Inner
def process(value: Parent[F]#Inner): Unit = {
val parent = value.parent
value match {
case parent.Foo(_) => println("integer")
case parent.Bar(_) => println("string")
}
}
}
val foo = (new Parent[List]).Foo(5)
val processer = new Parent[List]
processer.process(foo) // integer
如果将value.parent.Foo(_)
设为Inner#parent
,也可以使用val
作为模式。