如何参考更高种类的参数?

Luk*_*keG 5 generics scala

假设您具有以下特征:

trait Foo[A]{
    def foo: A
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个像这样的函数:

def getFoo[A <: Foo[_]](a: A) = a.foo
Run Code Online (Sandbox Code Playgroud)

Scala编译器推断出Any此函数的返回类型。如何_在的签名(或正文)中引用匿名参数getFoo?换句话说,如何取消匿名化参数?

我希望能够使用类似的功能

object ConcreteFoo extends Foo[String] {
  override def foo: String = "hello"
}

val x : String = getFoo(ConcreteFoo)
Run Code Online (Sandbox Code Playgroud)

由于明显的原因,编译失败,因为getFoo它隐式声明为Any

如果使用Scala(对于此问题而言为2.12)无法做到这一点,那么我会对这种限制的理性或技术原因感兴趣。我敢肯定有关于此的文章和现有问题,但是我似乎缺少正确的搜索词。


更新:现有答案可以正确回答我所说的问题,但是我想我对我的实际用例不够准确。对困惑感到抱歉。我想写

def getFoo[A <: Foo[_]] = (a: A) => a.foo

val f = getFoo[ConcreteFoo.type]

//In some other, unrelated place
val x = f(ConcreteFoo)
Run Code Online (Sandbox Code Playgroud)

因为我没有类型的参数A,所以编译器无法推断出参数RA如果

def getFoo[R, A <: Foo[R]]: (A => R) = (a: A) => a.foo
Run Code Online (Sandbox Code Playgroud)

喜欢建议。我想避免手动提供type参数RString在这种情况下),因为它感觉很多余。

And*_*kin 6

要从字面上回答您的确切问题:

def getFoo[R, A <: Foo[R]](a: A): R = a.foo
Run Code Online (Sandbox Code Playgroud)

但是,由于您没有使用任何类型A,因此实际上可以完全忽略它和<: Foo[..]绑定,只保留返回类型:

def getFoo[R](a: Foo[R]): R = a.foo
Run Code Online (Sandbox Code Playgroud)

更新(问题已被显着改变)

您可能走私了其他apply调用,该调用从单独的隐式返回类型见证程序推断出返回类型:

trait Foo[A] { def foo: A }

/** This is the thing that remembers the actual return type of `foo`
  * for a given `A <: Foo[R]`.
  */
trait RetWitness[A, R] extends (A => R)

/** This is just syntactic sugar to hide an additional `apply[R]()`
  * invocation that infers the actual return type `R`, so you don't
  * have to type it in manually.
  */
class RetWitnessConstructor[A] {
  def apply[R]()(implicit w: RetWitness[A, R]): A => R = w
}

def getFoo[A <: Foo[_]] = new RetWitnessConstructor[A]
Run Code Online (Sandbox Code Playgroud)

现在看起来几乎就像您想要的一样,但是您必须提供隐式的内容,并且必须getFoo[ConcreteFoo.type]()另外调用一对圆括号:

object ConcreteFoo extends Foo[String] {
  override def foo: String = "hey"
}

implicit val cfrw = new RetWitness[ConcreteFoo.type, String] {
  def apply(c: ConcreteFoo.type): String = c.foo
}

val f = getFoo[ConcreteFoo.type]()
val x: String = f(ConcreteFoo)
Run Code Online (Sandbox Code Playgroud)

我不确定这是否真的值得,这不一定是最直接的事情。带隐式的类型级计算,隐藏在一些微妙的语法糖之后:这两个隐藏在后面的魔术可能太多了()。除非您期望的返回类型foo会经常变化,否则可能更容易的是,仅向中添加第二个通用参数getFoo,然后显式地写出返回类型。