gpt4 book ai didi

Scala - 使用带有两个通用参数的父类(super class)型如何导致 Scala 类型检查器以不同方式对待子类型?

转载 作者:行者123 更新时间:2023-12-02 01:30:09 24 4
gpt4 key购买 nike

我遇到了一个有趣的情况。我想实现如下所示的东西。

object Test {
abstract class Key[A]
class Constraint[-A] {
def doSomething(a: A): String = ""
}

object DesiredKeyConstraints {
case class KeyConstraint[A](val key: Key[A], constraint: Constraint[A])
val data: Map[Key[_], KeyConstraint[_]] = Map()
}

def useTheKeyConstraints[A](key: Key[A], value: A): String = {
DesiredKeyConstraints.data.get(key).fold[String]("") {
case DesiredKeyConstraints.KeyConstraint(_, constraint) => constraint.doSomething(value)
}
}

def main(args: Array[String]) {
println("hi")
}
}

不幸的是,当我从映射中拉出一个 KeyConstraint 时,我不再知道它的类型。因此,当我尝试调用 doSomething 时,类型不会 check out 。这一切似乎都符合预期。有趣的是,在代码库的其他地方,我们有如下内容:(将 DesiredKeyConstraints 替换为 WorkingKeyConstraints)

object Test {
abstract class Key[A]
class Constraint[-A] {
def doSomething(a: A): String = ""
}

object WorkingKeyConstraints {
sealed trait SuperTrait[A, B] {
val key: Key[A]
}
case class KeyConstraint[A](val key: Key[A], constraint: Constraint[A]) extends SuperTrait[A, Unit]
val data: Map[Key[_], SuperTrait[_, _]] = Map()
}

def useTheKeyConstraints[A](key: Key[A], value: A): String = {
WorkingKeyConstraints.data.get(key).fold[String]("") {
case WorkingKeyConstraints.KeyConstraint(_, constraint) => constraint.doSomething(value)
}
}

def main(args: Array[String]) {
println("hi")
}
}

这个编译并运行得很好。出于某种原因,拥有父类(super class)型意味着当我们从 Map 中提取 KeyConstraint 时,它会将其视为 KeyConstraint[Any] 而不是 KeyConstraint[_] .因为 Constraint 是逆变的,所以我们可以将 Constraint[Any] 视为 Constraint[A],因此代码可以编译。这里的关键问题是,为什么拥有父类(super class)型会导致类型检查器将其视为 KeyConstraint[Any]

另外,作为进一步的信息,我进一步研究了这个,它是具有两个泛型类型参数的父类(super class)型所特有的。如果我用两个泛型类型做子类,或者用一个泛型类型做父类,它仍然会失败。在下面查看我的其他失败尝试:

object AnotherCaseThatDoesntWorkKeyConstraints {
case class KeyConstraint[A, B](val key: Key[A], constraint: Constraint[A])
val data: Map[Key[_], KeyConstraint[_, _]] = Map()
}

object AThirdCaseThatDoesntWorkKeyConstraints {
sealed trait SuperTrait[A] {
val key: Key[A]
}
case class KeyConstraint[A](val key: Key[A], constraint: Constraint[A]) extends SuperTrait[A]
val data: Map[Key[_], SuperTrait[_]] = Map()
}

我认为这是 Scala 类型检查器中的某种错误,但也许我遗漏了什么。

最佳答案

tl;dr 类型删除和模式匹配

使用 SuperTrait 键入 Map 会隐藏有关类型的信息,并导致模式匹配为您的提取器假定一个广泛的类型。


这是一个类似的示例,但使用的是 Any 而不是您的 SuperTrait。此示例还展示了如何从中生成运行时异常。

case class Identity[A : Manifest]() {
def apply(a: A) = a match { case a: A => a } // seemingly safe no-op
}

val myIdentity: Any = Identity[Int]()

myIdentity match {
case f@Identity() => f("string") // uh-oh, passed String instead of Int
}

抛出异常

scala.MatchError: string (of class java.lang.String)
at Identity.apply(...)

f@Identity() 模式匹配 Any 作为 Identity[Any],并且由于类型删除,这匹配 Identity[Int],变成了错误。


相反,如果我们将 Any 更改为 Identity[_]

case class Identity[A : Manifest]() {
def apply(a: A) = a match { case a: A => a }
}

val myIdentity: Identity[_] = Identity[Int]()

myIdentity match {
case f@Identity() => f("string")
}

正确地编译失败​​。

found   : String("string")
required: _$1 where type _$1
case f@Identity() => f("string")

它知道 f 是存在类型 Identity[T] forSome {type T},它无法显示 String符合通配符类型T


在第一个示例中,您有效地进行了模式匹配

DesiredKeyConstraints.KeyConstraint[Any](_, constraint)

第二个,有更多的信息,你匹配为

DesiredKeyConstraints.KeyConstraint[T](_, constraint) forSome {type T}

(这只是说明性的;您目前无法在模式匹配时实际编写类型参数。)

关于Scala - 使用带有两个通用参数的父类(super class)型如何导致 Scala 类型检查器以不同方式对待子类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34750973/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com