类型推断存在可用性问题 (2019)

Lobsters Hottest 新闻

摘要

这篇博文认为,编程语言中的类型推断虽然被广泛采用,但可能通过增加认知负担和妨碍代码理解而导致可用性问题。

<p><a href="https://lobste.rs/s/mfhss4/type_inference_has_usability_problems">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/09/08 07:01

# 类型推断存在可用性问题 来源:https://austinhenley.com/blog/typeinference.html ## Austin Z. Henley 我为人们构建工具 --- --- 2019年6月27日 **简言之:** 类型推断被众多工具和语言推广,但几乎没有证据表明它有益。我认为它可能妨碍理解并增加认知负荷。 *关于本文的讨论,请参见Hacker News上的帖子(https://news.ycombinator.com/item?id=20296123)。* 许多编程语言提供类型推断。新一波流行语言将其内置:Go (https://golang.org/)、Rust (https://www.rust-lang.org/)、Swift (https://swift.org/)、Scala (https://www.scala-lang.org/)、Kotlin (https://kotlinlang.org/)、Zig (https://ziglang.org/)、Nim (https://nim-lang.org/) 等。事实上,一些老旧语言后来也添加了此功能:C++11 (https://en.wikipedia.org/wiki/C%2B%2B11#Type_inference)、C# 3.0 (https://en.wikipedia.org/wiki/C_Sharp_3.0#Local_variable_type_inference) 和 Java 10 (https://en.wikipedia.org/wiki/Java_version_history#Java_10)。 根据维基百科 (https://en.wikipedia.org/wiki/Type_inference),类型推断指的是*"自动检测表达式数据类型"*。本质上,这意味着当类型可以从赋值中推断时,你无需显式声明变量类型。因此,与其写: ``` int x = 5; List<string> names = new List<string>(); ``` 你可以写: ``` var x = 5; var names = new List<string>(); ``` 那么好处是什么?维基百科 (https://en.wikipedia.org/wiki/Type_inference) 声称类型推断*"使许多编程任务变得更容易"*,但并未引用来源。使用类型推断的代码片段乍看之下确实更简洁。嗯,我们稍后会回到这一点。 你可能会想,*"好吧,如果它不好,我就不用它。"* 但如果你试图在 Go 中避免使用它,你会收到警告!Go 的代码检查工具提示你*"应从 var Y 的声明中省略类型 X;它将从右侧推断出来"*。此外,《Effective Go》(https://golang.org/doc/effective_go.html) 中的所有示例都遵循此实践。 ### 类型推断的代价 阅读代码时,我通常需要理解每个表达式的类型。因此,如果使用类型推断,我如何知道给定变量的类型? 如果是字面量,就很容易。如果变量或函数命名良好,通常也很直接。如果类型不明显,我可能需要导航到函数声明以查看返回类型。最坏的情况是我做出错误假设。 一些来自生产代码的例子: ``` var fieldName = "BuildingCode"; // 简单。 var entries = new Dictionary<string, object>(); // 简单。 var currentID = searchLog(fieldName)[0]; // 可以合理猜测。 var site = repo.GetSites().Where(x => x.SiteType == SiteTypes.Server); // 可以合理猜测。 var pdfMap = new Lazy<Map>(() => { MapEngine.CreateMap(this.x, this.y, this.zoom) }); // 并非如我所料。 ``` 工具对此有很大帮助。在大多数代码编辑器中,你可以将鼠标悬停在变量名上,它会显示类型提示。你也可以使用快捷键跳转到函数或类声明以深入查看。然而,这些看似快捷的操作可能带来额外成本:认知负荷 (https://en.wikipedia.org/wiki/Cognitive_load),即所需的心理努力。 想象被要求计算*3+9+15+18+5*。在纸上这微不足道。但如果口头询问,即使这个简单的算术也可能很费力,因为你必须主动记住表达式*并*进行计算。理解代码时已经有很多细节需要考虑。我不想因为必须记住变量类型或不断导航去查看它而使自己更加困难。 如果你不使用代码编辑器呢?如果你在 GitHub 上查看代码呢?你就没有类型信息了! ### 谨慎使用? 你当然不必总是使用类型推断。其他人已经为何时使用类型推断制定了最佳实践(例如,这些 (https://www.intertech.com/Blog/the-use-and-abuse-of-the-c-var-keyword/) 和这些 (https://softwareengineering.stackexchange.com/a/42893/37315))。另一位 (https://softwareengineering.stackexchange.com/a/271940/37315) 开发者列出了显式类型的好处,包括代码即文档和支持类似测试驱动开发的过程。微软似乎甚至改变了在 C# 中到处使用类型推断的立场。自2017年以来,Visual Studio 会建议*"使用显式类型代替 'var'"*。实际上,MSDN (https://docs.microsoft.com/en-us/visualstudio/ide/reference/convert-var-to-explicit-type?view=vs-2019) 解释使用显式类型以*"提高代码可读性"*,并建议将类型推断保留用于匿名类型。 ### 假定的好处:可读性和节省击键次数 有人认为 (https://typeinference.com/typing/2015/10/05/type_inference.html) 类型推断实际上通过移除代码中冗余的、妨碍理解的信息来提高可读性。此外,如果你确实需要检查类型,只需一两秒钟即可看到。 一个更激励人心的类型推断例子 (https://softwareengineering.stackexchange.com/a/257454/37315): ``` for (Map.Entry<String, Map<String, Integer>> row : table.entrySet()) { Integer rowKey = entry.getKey(); Map<String, Integer> rowValue = entry.getValue(); for (Map.Entry<String, Integer> col : rowValue.entrySet()) { Integer colKey = col.getKey(); SomeObject colValue = col.getValue(); doSomethingWith(rowKey, colKey, colValue); } } ``` 我的回应是?*去重构你那难看的代码吧。* 我听到的另一个较弱论点是,使用类型推断可以少打一些代码!因为我的打字速度绝对不是编码时的瓶颈。 ### 呼吁实证研究 在语言设计者盲目接受类型推断是好事之前,我建议进行一些研究。是的,这需要一些努力,但幸运的是,PL、SE 和 HCI 社区的研究人员非常聪明,一直在寻找有趣的项目!我希望将来发布我的研究设计想法。我还在打造 Knox 编程语言 (http://knoxlang.org/),以实验显式性优于简洁性(它仍处于非常早期的开发阶段!)。

相似文章

类型推断(第一部分)

Lobsters Hottest

关于类型推断的教程,涵盖Damas-Hindley-Milner类型系统、合一及相关概念,并附有OCaml代码示例。

记录类型推断入门指南

Lobsters Hottest

本文解释了静态类型语言中匿名记录类型推断的基础知识,使用了类型理论符号并以Haskell作为实现语言。

解析C语言中类型推断声明之险

Lobsters Hottest

这篇博文探讨了C23中涉及`auto`作为类型推断说明符或存储类说明符的解析歧义,展示了当`x`是typedef时,GCC和Clang在解析如`auto x = 67;`这样的声明上的分歧,以及属性如何使情况复杂化。