如何编写一个基于其类型参数的类型参数具有多态返回类型的函数



我有一些类似的代码:

sealed trait Foo[A] {
def value: A
}
case class StringFoo(value: String) extends Foo[String]
case class IntFoo(value: Int) extends Foo[Int]

我想要一个函数,它可以在给定子类型的类型参数的情况下使用A类型。

// Hypothetical invocation
val i: Int = dostuff[IntFoo](param)
val s: String = dostuff[StringFoo](param)

我不知道如何以一种有效的方式声明dostuff。我能想到的最接近的东西

def dostuff[B <: Foo[A]](p: Param): A

但这不起作用,因为A在那个位置是未定义的。我可以做一些类似的事情

def dostuff[A, B <: Foo[A]](p: Param): A

但我必须像dostuff[String, StringFoo](param)那样调用它,这非常难看。

编译器似乎应该拥有将A移动到返回类型所需的所有信息,我如何在标准scala或库中使其工作。如果这会影响答案的话,我目前使用的是scala2.10。我对2.11-唯一的解决方案持开放态度,如果在2.10 中可能但不可能

另一个选项是使用类型成员:

sealed trait Foo {
type Value
def value: Value
}
case class StringFoo(value: String) extends Foo { type Value = String }
case class IntFoo(value: Int) extends Foo { type Value = Int }
def dostuff[B <: Foo](p: Any): B#Value = ???
// Hypothetical invocation
val i: Int = dostuff[IntFoo](param)
val s: String = dostuff[StringFoo](param)

请注意,这两种解决方案主要围绕Scala中的语法限制工作,即不能修复列表的一个类型参数,而让编译器推断另一个。

如您所知,如果您有一个类型为Foo[A]的参数,那么您可以仅在A:中使该方法通用

def dostuff[A](p: Foo[A]): A = ???

由于情况可能并不总是如此,我们可以尝试使用一个隐式参数来表达AB之间的关系。由于我们不能只将一些泛型参数应用于方法调用(泛型参数推断要么全有要么全无),因此我们必须将其分为两个调用。这是一个选项:

case class Stuff[B <: Foo[_]]() {
def get[A](p: Param)(implicit ev: B => Foo[A]): A = ???
}

您可以检查REPL:中的类型

:t Stuff[IntFoo].get(new Param) //Int
:t Stuff[StringFoo].get(new Param) //String

另一个类似的选项,但使用匿名类,是:

def stuff[B <: Foo[_]] = new { 
def apply[A](p: Param)(implicit ev: B <:< Foo[A]): A = ??? 
}
:t stuff[IntFoo](new Param) //Int

在这里,我使用了apply而不是get,所以您可以更自然地应用该方法。此外,正如您的评论中所建议的,我在证据类型中使用了<:<。对于那些想了解更多关于这种类型的广义类型约束的人,你可以在这里阅读更多。

您也可以考虑在这里使用抽象类型成员而不是泛型参数。在处理泛型类型推理时,这通常提供了一个优雅的解决方案。您可以在这里阅读更多关于抽象类型成员及其与泛型的关系的信息。

最新更新