问题 基本型次要


请考虑 case class Foo[A, B <: List[A]](l: B) { ... } 或类似的东西。尤其是, A 以及 B 需要在身体的某个地方可用 Foo

编译器是否可以推断 A 自动?例如, Foo(List(1,2,3)) 类型检查器推断失败 A 如 Nothing。也许有一种方法可以通过使用类型成员来解决这个问题?

我有一种感觉,我忽略了一些非常简单的东西;)

编辑:我刚刚发现使用另一个类型参数 X 工作得很好,但atm我不明白为什么会这样:

scala> case class Bar[A, B[X] <: List[X]](l: B[A])
defined class Bar

scala> Bar(List(1,2,3))
res11: Bar[Int,List] = Bar(List(1, 2, 3))

有人可以向我解释一下吗? 这是一个统一问题吗?

编辑2:使用 [A, B[X] <: List[X]](l: B[A]) 可能会对某些层次结构产生不良影响(尽管这并不是什么大问题)。更有趣的是,我偶然发现了一个 博客文章 作者Josh Suereth隐晦地表明了这一点 [A, B <: List[A]](l: B with List[A]) 同样有效...不需要暗示等


4812
2018-01-05 19:56


起源

你能把课程定义为 case class Foo[A](l: List[A])?这可能会更清楚。 - leedm777
我理解你的意思,但这不适用于我的场景。 A型和B型必须彼此分开。 - fotNelton


答案:


它并没有真正回答“为什么”部分,对不起,但这里有一些你可以玩的技巧。首先,因为你没有使用 X 在你的例子中,你可以写:

case class Bar[A,B[_] <: Seq[_]](l : B[A])

接着:

scala> Bar(List(1,2,3))
resN: Bar[Int,List] = Bar(List(1, 2, 3))

(我正在使用协变 Seq 代替 List 显示它也适用于子类型。另请注意,这不等于使用额外的 X,请参阅注释。)不幸的是,每次要使用序列类型时,都需要编写 B[A],即手动实例化。解决这个问题的一种方法是改为:

case class Bar[A,B](l : B)(implicit ev : B <:< Seq[A])

在行动:

scala> Bar(List(1,2,3))
resN: Bar[Int,List[Int]] = Bar(List(1, 2, 3))

...而且你得到了类型参数 A 和 B 就像你一直都知道的那样实例化。


5
2018-01-05 22:34



这是一个糟糕的建议。不要写“B [] <:Seq []“,它并不意味着你的想法。他确实使用X,将B的类型参数链接到Seq的。下划线”B [] <:Seq []“不是同一类型。 - psp
除了 _ 问题,我在一篇有趣的帖子中发现了这一点,因为我从未猜到隐含的观点可能会有所帮助。那么编译器在哪里找到隐式视图 B 如 Seq[A]?当我在控制台中尝试时,它发现了Predef的一些功能,但它也适用于自定义类(显然在本节中没有可能有格式良好的示例,抱歉)。有没有办法跟踪编译器引入的含义? - fotNelton
啊,好的,看看Predef的来源揭示了<:<,=:=和<%<的“基础设施”。 - fotNelton
对不起,第一部分不太清楚。他们确实不相同,而且 B[_] <: Seq[_] 是真的 B[X] forSome { type X; } <: Seq[Y] forSome { type Y; }。我的意思是, 在这种情况下 单身是什么并不重要 X 或两个存在主义 _被分配给,因为你永远不会使用它们。就你所知 X 被分配给相当无用的人 Any,和我的两个存在 Nothing 和 Any 分别但是 B[A] 最重要的是什么。 - Philippe


编译器无法自动推断A.但如果有可能,就必须说A应该是a  Int,所以Int或Any,不只是Int!。因为List [Int] <:List [Any]。因此,如果编译器会推断Int为A,那么限制性太强。

换一种说法:

  1. case class Foo[A, B <: List[A]](l: B) { ... } 然后将其称为 Foo(List(1,2,3)) 你这么说 A必须是一个类型,其中包含它的列表应该是Int列表的超类型。 所以有效A必须是Int的超类型,因为List是协变的。

  2. case class Bar[A, B[X] <: List[X]](l: B[A]) 然后将其称为 Foo(List(1,2,3)) 你这么说 A必须是Int

案例(2)除了Int之外没有留给A的余地。 然而,情况(1)为A留下了除Int以外的空间,例如它可能是任何。 你可以看到这个,因为你可以通过它来调用它 Foo[Any, List[Int]](List(1,2,3))。在案例(2)中你将无法做到这一点。

所以这两个案例并不相同。


5
2018-01-08 01:19



谢谢,那 Foo[Any,List[Int]] 例子很好。 OTOH我想知道为什么,按照你的推理,仍然不可能说出类似的话 Foo[A, B >: List[A] <: List[A]] 因为从逻辑的角度来看,这会消除歧义(或者不是,我想知道?)。然而,在这两种情况下,即后者以及我原始帖子中提到的那种情况,编译器都会推断 A 如 Nothing 并且一直在抱怨(从我有限的观点来看可以理解)这就是为什么我认为除了协方差问题,这也可以用统一来解释? - fotNelton
是的,我按照你的推理。我想这就是scala的类型推断不完整的含义。 - Jan van der Vorst
这是非常好的 谈论 来自Daniel Spiewack关于(不仅是Scala的)不完全类型推断。介绍相当长,但整个讲座非常值得。 - fotNelton
为我的原始帖子添加了一些新的“见解”,以防你感兴趣。 - fotNelton


根据 阿列克谢罗曼诺夫原则上,类型可以自动推断,但目前还不是。

因此,至少目前,应该推断的所有类型参数都必须在参数中显式显示。


2
2018-02-14 06:39