为什么Scala允许嵌套数据结构,如List或Array

ali*_*rat 14 ocaml scala typing

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

 scala> List(1, List(1,2))
 res0: List[Any] = List(1, List(1, 2))
Run Code Online (Sandbox Code Playgroud)

如果你替换List,同样的事情Array.我在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
Run Code Online (Sandbox Code Playgroud)

那么为什么Scala允许这个编译呢?

Gab*_*lla 19

TL;博士

简而言之,OCaml和Scala使用两种不同类型的系统:前者具有结构类型,后者具有标称类型,因此它们在类型推断算法方面表现不同.


全面讨论

如果您允许在类型系统中使用名义子类型,那么这就是您所获得的.

在分析时List,Scala编译器将类型计算为列表包含的所有类型的LUB(最小上限).在这种情况下,的LUB IntListAny.其他情况会有更明智的结果:

@ List(Some(1), None)
res0: List[Option[Int]] = List(Some(1), None)
Run Code Online (Sandbox Code Playgroud)

的LUB Some[Int]NoneOption[Int],这通常是你所期望的.如果失败的话,对用户来说会"奇怪":

expected List[Some[Int]] but got List[Option[Int]]
Run Code Online (Sandbox Code Playgroud)

OCaml使用结构子类型,因此它的类型系统在类型推断方面的工作方式不同.正如@gsg在评论中指出的那样,OCaml特别不会统一像Scala这样的类型,但需要明确的upcast.

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

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

@ val x: List[Int] = List(1, List(1, 2))
Compilation Failed
Main.scala:53: type mismatch;
 found   : List[Any]
 required: List[Int]
}.apply
  ^
Run Code Online (Sandbox Code Playgroud)

每当编译器Any使用该-Ywarn-infer-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))
Run Code Online (Sandbox Code Playgroud)

  • OCaml支持子类型,但通过要求显式向下转换来避免推理问题. (2认同)

zig*_*tar 8

由于Scala允许隐式子类型,因此能够推断出具有混合内容的此类表达式的"正确"类型.Scala正确地推断出您的列表是类型的List[Any],这意味着它可以在其中发生任何事情.

由于Ocaml不支持隐式子类型而没有明确的向下转换; 它无法自动扩展混合列表的类型.

大多数情况下,如果你最终得到了类型,Any或者AnyRef你搞砸了某些东西,但在某些情况下它也是正确的.由程序员决定是否需要更严格的类型.