什么“不符合trait Builder的类型参数边界”在scala中意味着什么?

问题描述 投票:1回答:2

我有以下简单的程序,分别为类型参数和抽象类型别名定义2个相同的上限:

package scala.spike.typeBoundInference

object Example1 {
  trait Domain {
  }

  trait Impl {

    type DD <: Domain
    type GG <: StaticGraph[DD]

    type A1
    type A2
    type A3
    // ... this type list can go very long
    // so inlining them as generic type parameters is impossible


    final type Builder = StaticGraph.Builder[DD, GG]
  }

  trait DSL[I <: Impl] {

    val impl: StaticGraph.Builder[I#DD, I#GG]
  }

  trait StaticGraph[T <: Domain] {}

  object StaticGraph {

    trait Builder[D <: Domain, G <: StaticGraph[D]] {}

  }
}

但是,scala拒绝编译它:

错误:(16,27)类型参数[I#DD,I#GG]不符合trait Builder的类型参数bounds [D <:scala.spike.typeBoundInference.Example1.Domain,G <:scala.spike.typeBoundInference。 Example1.StaticGraph [D]] val impl:StaticGraph.Builder [I#DD,I#GG]

这可能会出错?

  • DD <:域名检查
  • GG <:StaticGraph [DD]检查

斯卡拉没有理由认为它不安全。

在此期间,我发现如果将类StaticGraph [T]声明为协变scala编译器将成功运行。这更糟糕(由于某种原因,StaticGraph [T]必须是不变的),因为类型绑定GG <:StaticGraph [DD]意味着如果确定了类型DD,那么GG是StaticGraph [DD]的子类,但不是必需的StaticGraph [Domain]的子类,这正是我想要的。

更新1:我已经阅读了所有的答案和评论,并以某种方式得到的印象是,核心原因是无法保证i的任何实例Impl,类型绑定只保证类型

i.DD <:< Impl#DDImp#GG <:< StaticGraph[Impl#DD]

但不是StaticGraph[i.DD] <:< StaticGraph[Impl#GG]

因此i.GG <:< StaticGraph[i.DD]也不能保证。

但是,我已经做了一个快速的实验来验证这个想法,结果证明这不是真的:

object Example1 {

  trait Domain {}
  class D1 extends Domain {}

  trait Impl {

    type DD <: Domain
    type GG <: StaticGraph[DD]
  }

  class StaticGraph[T <: Domain] {}

  object Impl1 extends Impl {

    type DD = D1
    type GG = StaticGraph[Domain]
  }

  //or this:

  val impl = new Impl {

    type DD = D1
    type GG = StaticGraph[Domain]
  }
}

在这种情况下编译器抛出一个错误:

错误:(19,10)在特征Impl中覆盖类型GG,边界为<:scala.spike.TypeBoundInference.Example1.StaticGraph [scala.spike.TypeBoundInference.Example1.Impl1.DD];类型GG具有不兼容的类型类型GG = StaticGraph [Domain]

如果您认为类型约束不适用于某些情况,您能举例说明吗?

更新2:事实证明,根据答案,这是真的:

i.GG <:< StaticGraph[i.DD]

但这可能是假的:

Impl#GG <:< StaticGraph[Impl#GG]

所以在DSL的背景下,这也可能是错误的:

I#GG <:< StaticGraph[I#GG](3)

但这只是谜题的一部分,为了证明它是不安全的类型,我们必须构建一个DSL [I]的反例,它使条件(3)无效。所以旧的问题仍然存在:是否有可能构建一个反例?

scala types type-systems generic-type-argument abstract-type
2个回答
2
投票

这可能会出错?

GG <:StaticGraph [DD]检查

通过声明type GG <: StaticGraph[DD],您可以在成员类型之间建立关系(它与<: StaticGraph[this.DD]相同)。这意味着您需要考虑Impl的实例。

对于任何val i: Impl,你有i.DD <: Domaini.GG <: StaticGraph[i.DD]。你也有i.DD <: I#DD。但你没有i.DD =:= I#DD!所以StaticGraph[i.DD]StaticGraph[I#DD]没有关系(对于不变的StaticGraph)。所以i.GG(或I#GG)和StaticGraph[I#DD]都不是。

要使其编译,您需要要求所有i.DD都相同(这也保证i.DD =:= I#DD)。有一种方法可以做到这一点:

trait DSL[T <: Domain, I <: Impl { type DD = T } ] 

将使代码编译(没有任何其他更改)。

如果StaticGraph是协变的,那么关系就会成功:

I#GG =:= (kind of)
i.GG forSome { val i: I } <:
StaticGraph[i.DD] forSome { val i: I } <:
StaticGraph[I#DD] forSome { val i: I } =:=
StaticGraph[I#DD]

0
投票

好问题解决了:

import scala.language.higherKinds

object Example5 {

  trait Domain {}
  trait D1 extends Domain

  trait Impl {

    type DD <: Domain
    type GG[T <: Domain] <: StaticGraph[T]
  }

  trait DSL[I <: Impl] {

    val impl: Builder[I#DD, I#GG]
  }

  trait StaticGraph[T <: Domain] {}

  trait Builder[D <: Domain, G[T <: Domain] <: StaticGraph[T]] {}
}

我无法相信我必须使用更高级别的这种平庸的事情: - <

为什么编译?它解耦类型约束并将其延迟到必要时。 (这是我能想到的唯一解释)

© www.soinside.com 2019 - 2024. All rights reserved.