【问题标题】:How to infer the right type parameter from a projection type?如何从投影类型推断正确的类型参数?
【发布时间】:2013-02-06 03:45:08
【问题描述】:

让 Scala 从类型投影中推断出正确的类型时遇到了一些麻烦。

考虑以下几点:

trait Foo {
  type X
}

trait Bar extends Foo {
  type X = String
}

def baz[F <: Foo](x: F#X): Unit = ???

然后以下编译正常:

val x: Foo#X = ???    
baz(x)

但以下内容无法编译:

val x: Bar#X = ???    
baz(x)

Scala 看到x 的“基础类型字符串”,但丢失了xBar#X 的信息。如果我注释类型,它可以正常工作:

baz[Bar](x)

有没有办法让 Scala 为 baz 推断正确的类型参数?
如果不是,一般的答案是什么?

【问题讨论】:

  • 不是答案,但值得注意的是,如果您使用类型指示符而不是类型投影键入 x,它会起作用 - 包括例如object BAR extends Bar; val x: BAR.X = "a"; baz(x).
  • 另外值得注意的是:你可以让编译器相信你真的确实希望 x 被键入为或多或少类似于 Bar#X 的东西,带有难以置信的丑陋的 @987654335 @.
  • 看起来很奇怪的用例。为什么要这样做?
  • @JesperNordenberg 用例来自banana-rdf。如果我直接使用RDF trait 的子类型,那么我无法从this one 等许多隐式函数中受益。

标签: scala polymorphism type-inference type-projection


【解决方案1】:

程序通过在上下文中添加这个隐式转换来编译:

implicit def f(x: Bar#X): Foo#X = x

由于这种隐式转换对于任何F &lt;: Foo 都是正确的,我想知道为什么编译器不自己这样做。

【讨论】:

    【解决方案2】:

    您还可以:

    trait Foo {
        type X
    }
    trait Bar extends Foo {
        type X = String
    }
    class BarImpl extends Bar{
      def getX:X="hi"
    }
    def baz[F <: Foo, T <: F#X](clz:F, x: T): Unit = { println("baz worked!")}
    val bi = new BarImpl
    val x: Bar#X = bi.getX
    baz(bi,x)
    

    但是:

    def baz2[F <: Foo, T <: F#X](x: T): Unit = { println("baz2 failed!")}
    baz2(x)
    

    失败:

    test.scala:22: error: inferred type arguments [Nothing,java.lang.String] do not conform to method baz2's type parameter bounds [F <: this.Foo,T <: F#X]
    baz2(x)
    ^
    one error found
    

    我认为基本上,F <: foo f x>特定 X 来自哪个类。您的 X 只是一个字符串,不保留指向 Bar 的信息。

    注意:

    def baz3[F<: Foo](x : F#X) = {println("baz3 worked!")}
    baz3[Bar]("hi")
    

    也有效。你定义了一个 val x:Bar#X=???就是那个意思???仅限于 Bar#X 在编译时可能发生的任何情况...编译器知道 Bar#X 是字符串,因此 x 的类型只是一个字符串,与任何其他字符串没有区别。

    【讨论】:

    • 是的,编译器只看到String 并丢失Foo 上下文,尽管使用Bar#X 而不是String 注释类型。到目前为止,我唯一的解决方案是始终抽象 Foo 并在最后提供 Bar。但这对我来说确实是个问题,因为它限制了我可以做的事情baz...(在我的情况下,baz 也是隐含的)
    • 对。你可以这样想:类型 X 是 Foo 的成员,有点像 Java 中的其他静态变量和成员变量。试图在没有实例的情况下访问它就像试图在没有实例的情况下访问成员变量。
    • 在:trait HasType{type X} class Foo(val t:HasType){type A=t.X}。您可以有许多 Foo 和不同的 A,每个都依赖于特定传递的 tHasType 的实例)。类型是动态的,因此必须以某种方式指定。此处示例中的 Foo#A 不会告诉您或编译器更多关于 A 的信息,而 Person.age 不会告诉您有关人员实例的年龄。实例t 提供类型信息。
    • 也许你可以静态地做一些事情(在 scala 中这意味着伴随对象)? object Foo { type X = String } class Foo; val s:Foo.X="hi" ?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-15
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    • 1970-01-01
    • 2023-02-26
    相关资源
    最近更新 更多