为什么Scala允许像列表或数组这样的嵌套数据结构

为什么Scala允许像列表或数组这样的嵌套数据结构,scala,ocaml,typing,Scala,Ocaml,Typing,为什么像Scala这样具有非常强的静态类型系统的语言允许以下构造: scala> List(1, List(1,2)) res0: List[Any] = List(1, List(1, 2)) 如果将列表替换为数组,则同样有效。我学习了OCaml中的函数式编程,它会在编译时拒绝相同的代码: # [1; [1;2]; 3];; Characters 4-9: [1; [1;2]; 3];; ^^^^^ Error: This expression has type '

为什么像Scala这样具有非常强的静态类型系统的语言允许以下构造:

 scala> List(1, List(1,2))
 res0: List[Any] = List(1, List(1, 2))
如果将
列表
替换为
数组
,则同样有效。我学习了OCaml中的函数式编程,它会在编译时拒绝相同的代码:

# [1; [1;2]; 3];;
Characters 4-9:
  [1; [1;2]; 3];;
      ^^^^^
Error: This expression has type 'a list
       but an expression was expected of type int

那么Scala为什么允许编译它呢?

因为Scala允许隐式子类型,所以它能够推断出这种混合内容表达式的“正确”类型。Scala正确地推断出您的列表属于
list[Any]
类型,这意味着其中可能发生任何事情

因为Ocaml不支持没有显式向下转换的隐式子类型;无法自动加宽混合列表的类型

最常见的情况是,如果您以键入
Any
AnyRef
结束,您会弄糟一些东西,但在某些情况下它也可能是正确的。由程序员决定是否需要更严格的类型。

tl;博士 长话短说,OCaml和Scala使用两类不同的类型系统:前者有,后者有,因此在类型推理算法方面,它们的行为不同


充分讨论 如果你允许在你的类型系统,这几乎是你得到的

分析
列表时
,Scala编译器将该类型计算为该列表包含的所有类型的LUB(最小上界)。在这种情况下,
Int
List
的LUB是
Any
。其他情况会产生更合理的结果:

@ List(Some(1), None)
res0: List[Option[Int]] = List(Some(1), None)
Some[Int]
None
的润滑油是
Option[Int]
,这通常是您所期望的。如果此操作失败,用户会感到“奇怪”:

expected List[Some[Int]] but got List[Option[Int]]
OCaml使用,因此在进行类型推断时,其类型系统的工作方式不同。正如@gsg在评论中指出的,OCaml并没有像Scala那样统一类型,而是需要显式的向上转换

在Scala中,编译器在执行类型推断时统一类型(由于名义上的子类型)

当然,使用显式类型注释可以获得更好的错误:

@ val x: List[Int] = List(1, List(1, 2))
Compilation Failed
Main.scala:53: type mismatch;
 found   : List[Any]
 required: List[Int]
}.apply
  ^
每当编译器使用
-Ywarn infer Any
标志推断出
Any
(这通常是一个坏符号)时,您都会收到警告。下面是scala REPL的一个示例:

scala -Ywarn-infer-any
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_51).
Type in expressions to have them evaluated.
Type :help for more information.

scala> List(1, List(1, 2))
<console>:11: warning: a type was inferred to be `Any`; this may indicate a programming error.
       List(1, List(1, 2))
            ^
res0: List[Any] = List(1, List(1, 2))
scala-Ywarn推断任何
欢迎使用Scala版本2.11.7(Java热点(TM)64位服务器虚拟机,Java 1.8.0_51)。
键入要计算的表达式。
键入:有关详细信息的帮助。
scala>List(1,List(1,2))
:11:警告:类型被推断为“Any”;这可能表示编程错误。
清单(1,清单(1,2))
^
res0:List[Any]=List(1,List(1,2))

一些可能对您有用的附加链接:,来自shapeless。Scala的类型系统只有在您眯着眼睛看时才显得“强大”。阅读和。@ChrisMartin链接文章中的哪一篇支持您的断言?显式的
Null
类型(以及
Nothing
类型,以及完全适合类型层次结构的基本类型)是对Java的明显改进。使用Scala时污染堆要困难得多,因为你不需要经常使用编译器——正是因为Scala的类型系统比Java的更丰富。@SillyFreak“Strong”有许多定义(其中一些Scala确实满足),但“比Java更好”不是其中之一。@ChrisMartin空类型不会使类型系统变弱,堆污染不是Scala的问题,而是Java的问题。在我看来,您只是在反对Java,我只是简单地指出,您没有解决Scala类型系统的任何弱点。我最初的问题是:“链接文章中的哪一篇支持你的断言?”谢谢你的解释,我对这些情况更清楚了。OCaml支持子类型,但通过要求显式降级来避免推理问题。@gsg哦,谢谢,这很有趣。这肯定证明了我不懂OCaml:)@gsg我做了家庭作业,对答案做了一些调整。感谢大家的直言不讳,OCaml根本不允许任何降级。它只允许向上投射,但即使向上投射也必须是明确的。主要区别在于,Scala使用“局部类型推理”,与Haskell和OCaml中使用的基于Hindley-Milner类型系统的实类型推理系统相比。OP示例在HM型系统中是不可判定的。这就是Scala坚持使用本地类型推断的原因。感谢您的解释,我对这些情况有了更清楚的了解。另一种类型是
java.lang.Serializable
。同样,OCaml支持子类型。这就是为什么它的名字中有
O
。它只是喜欢显式的而不是隐式的。