亚历克西斯·金(Alexis King)刚刚发表了一篇很棒的博客文章,标题为“不,动态类型系统本质上并不开放”。
这让我想起了我去年在FOSDEM上发表的名为“极简主义与类型”的演讲,在该演讲中,我从稍微不同的角度主张静态类型。我试图说服人们,动态类型程序中的类型通常比人们意识到的要复杂。而且通常比典型的静态类型语言复杂。
人们经常说相反的话,静态类型系统更复杂,而动态类型语言则更简单。从表面上看,这似乎是对的:在动态世界中,您只是轻松地声明变量,分配值并使用它们进行处理,而不必写下任何类型,无论它们多么琐碎或复杂。在打字部门,事情比“什么都不做”要简单得多,对吧?
好吧,类型不过是数据的形状和允许的行为而已。因此,这并不意味着您在任何程序中都没有形状和行为,而与语言无关……因此,无论是否编写代码,都拥有类型。即使在汇编语言中,即使在概念上,它们也都存在,它们是程序可以操纵的“有效值”集合。您必须考虑它们,它们必须有意义,并且它们必须做正确的事情。因此,简而言之,在正确的动态类型程序中,类型必须与在静态类型中的类型一样正确(否则会出现运行时错误)。
换句话说,类型在那里,但是您必须在头脑中运行类型检查器。您知道有趣的事吗?当人们不写下类型时,他们最终往往会得到比写它们的人更复杂的类型。复杂性只是在雷达之下,因此堆积如山。
有一天,您打开了六个月没有接触过的模块,并且看到了一个函数调用,其中第三个参数为空。您需要记住可以传递给第三个参数的变量类型,或者阅读整个源代码以弄清楚该变量。您可以通过代码来查看使用第三个参数的所有位置,并认识到第三个参数的可接受类型取决于您赋予第二个参数的内容。恭喜,您正在处理从属类型,这意味着您在类型系统复杂性方面已经超过了Haskell。处理这种类型系统的编译器是如此复杂,它们是有效的证明助手(并且处于编程语言研究的最前沿),在这里您正在用脑来处理这些数据类型(以及对自己的能力的信念)进行足够的测试)。
鉴于没有机械类型检查器来规定可表达的内容,并且只要程序不会崩溃,动态运行时就可以接受任何内容,因此当您脑海中进行类型检查时,您实际上拥有世界上功能最强大,最复杂的类型检查器您的处置。一旦开始利用这种能力,您就不得不面对世界上最复杂的类型系统。
当您赋予人们表达能力时,他们就会使用它。以我的经验,人们疯狂地使用动态语言来构造复杂的结构,而通常不会使用静态语言来构造。并不是说静态语言的功能不那么强大(等价,等等),而是它们使您正在做的事情对您更加明显(Alexis的帖子中有一些很好的例子)。在动态类型的程序中,人们都渴望使变量和数据结构执行双重或三重职责(“在Z情况下,这是X但在Y时也是Y”),但是当他们不得不写下正在做的事情时作为类型,这就像进行一点良心检查一样,在为可以用更简单方式描述的事物创建更复杂的类型之前,他们三思而后行(简单的示例:他们可能会制作两个简单的函数,而不是制作一个使用更改其他参数行为的字符串参数)。静态类型会向您推销更简单,更少“聪明”的解决方案(从长远来看,我们都知道哪种解决方案更可维护)。
但是,好的,我们假设我们避免使用“聪明”方法,并在其中任一方法中选择相同的解决方案。用静态或动态语言编写相同的程序以相同的方式处理相同的数据,您最终将在两者中得到大致相同类型的值。变量是否具有静态类型这一事实不会改变这一点。
“但是使用动态语言,我不必编写类型!少工作了!”
“不必”写类型,但是无论如何都要思考它们,就像在做数学“不必”写下任何东西,然后在脑海中进行所有计算一样。在进行复杂的计算时不使用笔和纸来追踪思路有什么好处,而是被限制为仅在脑海中进行计算?如果没有机械工具(例如可以实际计算您所写下的东西的计算器)来检查用笔和纸写的东西是否有意义,这又有什么优势呢?
我很懒,所以我讨厌脑子里做任何数学运算。我会记下来,并在一周中的任何一天都有机器为我检查。为什么在编程时我不想要相同的东西?那就是计算机的用途,对不对?使我们免于计算大脑中的事物。因此,我将编写我的类型,并让编译器检查它们是否有意义,非常感谢。减少了工作量。