存在高级类方法参数
Existential Higher Kinded Method Parameter
我定义了以下类型:
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition[A[_ <: Context] <: Attribute[C] forSome { type C <: Context }] {
def read[C <: Context]: A[C]
}
我想要 AttributeDefinition
到 return 一个由上下文参数化的 Attribute
。每个定义仅适用于一种类型的 Attribute
。假设:
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition extends AttributeDefinition[ConstantValueAttribute] {
override def read[C <: Context]: ConstantValueAttribute[C] = ???
}
这完全可以编译,但我在制作接受无限 AttributeDefinition
的方法签名时遇到了问题。以及创建 AttributeDefinition
的集合。以下所有失败:
def def1(attribute: AttributeDefinition): Unit = ???
def def2(attribute: AttributeDefinition[_]): Unit = ???
def def3(attribute: AttributeDefinition[_ <: Attribute[C] forSome { type C <: Context }]): Unit = ???
def def4(attribute: AttributeDefinition[A[_]] forSome { type A <: Attribute[C]; type C <: Context })
编辑:
我正试图找到一个像上面这样的可以编译的方法声明。我也想弄清楚如何声明 AttributeDefinition
s.
的集合
val val1 = new mutable.ArrayBuffer[AttributeDefinition]
如果这不可能,我很想弄清楚如何简化上面的特征,它仍然可以捕获简单的读取方法声明。
正确的签名应该是
def `def`[A[_ <: Context] <: Attribute[C] forSome { type C <: Context }](attribute: AttributeDefinition[A]) = ???
您需要为您的函数使用类型参数,它应该(至少)具有与您的类型相同的约束。
注意:在我看来,这就是为什么你应该避免对你的类型的类型参数施加类型约束的原因,它们应该只放在 functions/operations 上。
trait Context
trait Attribute[+Context]
trait AttributeDefinition[Attribute] {
def read[Context]: Attribute
}
class ConstantValueAttribute extends Attribute[Context]
object ConstantValueAttributeDefinition extends AttributeDefinition[ConstantValueAttribute] {
def read[Context]: ConstantValueAttribute = ???
}
def def1[C](attribute: AttributeDefinition[C]) : Unit = ???
作为简化的提示。
具有类型成员的提案
本质上,您在 defN
声明和 Seq
集合 AttributeDeclaration
中想要的是 Attribute
的更高种类的存在类型,用作AttributeDeclaration
的参数。因此,理想情况下,您希望:
def def1(attrDef: AttributeDeclaration[A] forSome {
type A[C <: Context] <: Attribute[C] }): Unit = ???
但是,这个 does not work, and this is an unresolved issue since 2015,所以很可能直到 dotty 才会修复它。
与其试图强制编译器处理更高种类的存在性以便您可以丢弃不必要的类型参数,我建议您首先不要添加烦人的类型参数。考虑这些定义(使用 2.12.4 编译):
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition {
type A[C <: Context] <: Attribute[C]
def read[C <: Context]: A[C]
}
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition extends AttributeDefinition {
type A[C <: Context] = ConstantValueAttribute[C]
def read[C <: Context]: ConstantValueAttribute[C] = ???
}
def def1(attrDef: AttributeDefinition): Unit = ???
val collDefs: Seq[AttributeDefinition] = List(ConstantValueAttributeDefinition)
这些定义具有以下属性:
Context
和 Attribute
保持不变
A
变成了AttributeDefinition
的类型成员,所以以后不用被existential强行擦除
read
-签名完全没变,字面上和你原来的代码一样,一个字符对一个字符
- 最简单和最直观的声明
def1
无需更改即可立即工作(也基本上从您的代码中复制 1:1)
- 使用
AttributeDefinition
声明和定义集合也同样顺利。
回想一下,如果将某些内容从类型参数移动到类型成员,您甚至不会丢失任何内容,因为您始终可以通过名称引用类型成员(例如,当您想要对其施加额外限制时类型成员)。以下最小示例应该演示如何对类型成员施加额外限制(与您的代码模糊相关,使用 Context
):
trait Foo {
type Member
}
def restrictiveFoo(x: Foo { type Member <: Context }): Unit = ???
在这里,您可以强制参数 Foo
具有派生自 Context
的类型成员。它与 type Member = Context
类似。我想以此证明的是:如果你引入类型成员,它并不是永远隐藏在特征中。你可以用它们做几乎所有与类型参数相同的事情。总结:
- 在(更高种类的)类型 member 上添加额外的约束很容易
- 删除具有更高种类存在性的多余类型参数似乎根本不起作用。
因此,我建议使用类型成员。
方差从 A
更改为 +A
的提案
另一个quick-fix使其编译:
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition[+A[X <: Context] <: Attribute[X]] {
def read[C <: Context]: A[C]
}
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition
extends AttributeDefinition[ConstantValueAttribute] {
override def read[C <: Context]: ConstantValueAttribute[C] = ???
}
class FooValueAttribute[C <: Context] extends Attribute[C]
object FooValueAttributeDefinition
extends AttributeDefinition[FooValueAttribute] {
override def read[C <: Context]: FooValueAttribute[C] = ???
}
def def1(attrDef: AttributeDefinition[Attribute]): Unit = {}
def1(ConstantValueAttributeDefinition)
val listOfDefs: List[AttributeDefinition[Attribute]] = List(
ConstantValueAttributeDefinition,
FooValueAttributeDefinition
)
受到 this question 答案的启发。奇怪的是,这似乎是几乎完全相同的星座。它并没有解决更高种类存在的问题,而是 A
的方差已更改为 +A
,因此可以使用 Attribute
作为所有具体 A
s.
我定义了以下类型:
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition[A[_ <: Context] <: Attribute[C] forSome { type C <: Context }] {
def read[C <: Context]: A[C]
}
我想要 AttributeDefinition
到 return 一个由上下文参数化的 Attribute
。每个定义仅适用于一种类型的 Attribute
。假设:
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition extends AttributeDefinition[ConstantValueAttribute] {
override def read[C <: Context]: ConstantValueAttribute[C] = ???
}
这完全可以编译,但我在制作接受无限 AttributeDefinition
的方法签名时遇到了问题。以及创建 AttributeDefinition
的集合。以下所有失败:
def def1(attribute: AttributeDefinition): Unit = ???
def def2(attribute: AttributeDefinition[_]): Unit = ???
def def3(attribute: AttributeDefinition[_ <: Attribute[C] forSome { type C <: Context }]): Unit = ???
def def4(attribute: AttributeDefinition[A[_]] forSome { type A <: Attribute[C]; type C <: Context })
编辑:
我正试图找到一个像上面这样的可以编译的方法声明。我也想弄清楚如何声明 AttributeDefinition
s.
val val1 = new mutable.ArrayBuffer[AttributeDefinition]
如果这不可能,我很想弄清楚如何简化上面的特征,它仍然可以捕获简单的读取方法声明。
正确的签名应该是
def `def`[A[_ <: Context] <: Attribute[C] forSome { type C <: Context }](attribute: AttributeDefinition[A]) = ???
您需要为您的函数使用类型参数,它应该(至少)具有与您的类型相同的约束。
注意:在我看来,这就是为什么你应该避免对你的类型的类型参数施加类型约束的原因,它们应该只放在 functions/operations 上。
trait Context
trait Attribute[+Context]
trait AttributeDefinition[Attribute] {
def read[Context]: Attribute
}
class ConstantValueAttribute extends Attribute[Context]
object ConstantValueAttributeDefinition extends AttributeDefinition[ConstantValueAttribute] {
def read[Context]: ConstantValueAttribute = ???
}
def def1[C](attribute: AttributeDefinition[C]) : Unit = ???
作为简化的提示。
具有类型成员的提案
本质上,您在 defN
声明和 Seq
集合 AttributeDeclaration
中想要的是 Attribute
的更高种类的存在类型,用作AttributeDeclaration
的参数。因此,理想情况下,您希望:
def def1(attrDef: AttributeDeclaration[A] forSome {
type A[C <: Context] <: Attribute[C] }): Unit = ???
但是,这个 does not work, and this is an unresolved issue since 2015,所以很可能直到 dotty 才会修复它。
与其试图强制编译器处理更高种类的存在性以便您可以丢弃不必要的类型参数,我建议您首先不要添加烦人的类型参数。考虑这些定义(使用 2.12.4 编译):
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition {
type A[C <: Context] <: Attribute[C]
def read[C <: Context]: A[C]
}
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition extends AttributeDefinition {
type A[C <: Context] = ConstantValueAttribute[C]
def read[C <: Context]: ConstantValueAttribute[C] = ???
}
def def1(attrDef: AttributeDefinition): Unit = ???
val collDefs: Seq[AttributeDefinition] = List(ConstantValueAttributeDefinition)
这些定义具有以下属性:
Context
和Attribute
保持不变A
变成了AttributeDefinition
的类型成员,所以以后不用被existential强行擦除read
-签名完全没变,字面上和你原来的代码一样,一个字符对一个字符- 最简单和最直观的声明
def1
无需更改即可立即工作(也基本上从您的代码中复制 1:1) - 使用
AttributeDefinition
声明和定义集合也同样顺利。
回想一下,如果将某些内容从类型参数移动到类型成员,您甚至不会丢失任何内容,因为您始终可以通过名称引用类型成员(例如,当您想要对其施加额外限制时类型成员)。以下最小示例应该演示如何对类型成员施加额外限制(与您的代码模糊相关,使用 Context
):
trait Foo {
type Member
}
def restrictiveFoo(x: Foo { type Member <: Context }): Unit = ???
在这里,您可以强制参数 Foo
具有派生自 Context
的类型成员。它与 type Member = Context
类似。我想以此证明的是:如果你引入类型成员,它并不是永远隐藏在特征中。你可以用它们做几乎所有与类型参数相同的事情。总结:
- 在(更高种类的)类型 member 上添加额外的约束很容易
- 删除具有更高种类存在性的多余类型参数似乎根本不起作用。
因此,我建议使用类型成员。
方差从 A
更改为 +A
的提案
另一个quick-fix使其编译:
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition[+A[X <: Context] <: Attribute[X]] {
def read[C <: Context]: A[C]
}
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition
extends AttributeDefinition[ConstantValueAttribute] {
override def read[C <: Context]: ConstantValueAttribute[C] = ???
}
class FooValueAttribute[C <: Context] extends Attribute[C]
object FooValueAttributeDefinition
extends AttributeDefinition[FooValueAttribute] {
override def read[C <: Context]: FooValueAttribute[C] = ???
}
def def1(attrDef: AttributeDefinition[Attribute]): Unit = {}
def1(ConstantValueAttributeDefinition)
val listOfDefs: List[AttributeDefinition[Attribute]] = List(
ConstantValueAttributeDefinition,
FooValueAttributeDefinition
)
受到 this question 答案的启发。奇怪的是,这似乎是几乎完全相同的星座。它并没有解决更高种类存在的问题,而是 A
的方差已更改为 +A
,因此可以使用 Attribute
作为所有具体 A
s.