类型是软件设计的基本工具


本文来自tedinski,类型与函数哪个是设计基础?数据结构和算法是设计基础吗?
函数与类型的关系,打个比喻,有点像建筑师想要进行室内设计。并不是说房间的内部并不重要 - 一个糟糕的建筑师绝对可以创造人类无用的房间 - 但那不是重要的部分。家具可以重新布置,承重墙不能。
我们可以编写好一些或差一些的函数,但内部细节与软件的整体设计并不真正相关。它应该是孤立的。
如果删除函数体以后剩余什么内容?

  • 剩下的一切都只是类型。 即使在动态语言中,它们也只是不太详细的类型:具有某些方法的类,具有一定数量参数的函数,以及与这些事物相关的文档。
  • 任何全局状态都可以在任何地方使用,因此很难猜测任何函数如何输出结果的。缺少全局状态,函数正在处理的唯一事物是其类型签名。
  • 一个函数应该倾向于对其实际声明的参数进行操作,而不是“从它的参数中可以获得的任何东西”。

人类喜欢为事物命名。这是我们如何理解世界的一部分。在费曼的一本书中,他开玩笑说只是知道某事的名字是无用的,但我认为他是错的。名字就是开始:这是你开始理解的想法。它是与其他人交流和查找的工具。在编程中,我们给出的所有名称都是一种类型,或与之相关联。

我最近帮助一个孩子学习写一些Python。他们遇到了一个调用接口的情况 - 但Python并没有真正的接口。没有要求继承,因为甚至没有任何可共享实现供参考。在感觉有点愚蠢一分钟之后,我最后建议他们在评论中写一个“模板”类。至少这给了他们一些复制和粘贴的东西,才能启动那个非接口的每个新实现。

当你不能给重要的东西命名时,真的很烦人。

当然,当你必须为更具结构性的东西提供名称时,同样令人厌烦。想想Java缺乏函数类型。


从类型的角度思考设计
UML尽管有些缺点,至少让这一部分是正确。在设计方面,我们想弄清楚类型是什么,它们之间的关系是什么,并给它们起名字。

C程序员最终得到了类似的设计方法,我通常称之为“表示法representations first.”.C有一个足够原始的类型系统,类型实际上更多地是为了表示而不是其他任何东西。

Kernighan和Pike写道:“数据结构的设计是创建程序的核心决定。一旦布局了数据结构,算法就会落到适当的位置,编码也相对容易。”

来自“编程实践”:这种观点的一个方面是编程语言的选择对整体设计而言相对不重要。我们将在抽象中设计程序,然后用C,Java,C ++,Awk和Perl编写它。

我不同意!首先,将“数据结构”与“类型”等同起来是一种非常C的思维方式。该书后面的例子太简单了,无法对其进行真正批评,但总的来说,他们在这里表达的观点有时被嘲讽地称为“你可以用任何语言写C!”

这是玩笑者的抱怨,当然还有“你可以用任何语言编写Fortran”。我想我现在有时会听到人们抱怨:“可以用任何语言编写Java”,这并不奇怪。

函数编程范式给了我们标准ML和Haskell的分支,也是非常注重类型优先编程。Haskell最基本的创新之一(嗯,我猜它可能不是第一个,但与其他相对主流的语言相比)是能够编写函数类型而不是编写函数体。

map :: (a -> b) -> [a] ->
...

一方面,我经常感受到这种语言设计的好处。在写一个函数体之前写下函数的类型(或更典型地,函数类型的整个集合)是非常好的。它可以让您思考并做一些有益的计划。Haskell程序员通过编写函数类型开始是有原因的,即使它可以被推断出来。

另一方面,我偶尔会遗漏一个函数参数缺少显式名称。虽然我可能没有map特别记住参数顺序的问题,但是当你的IDE可以map(fn, lst)快速提醒你函数的参数和顺序时,这是非常好的。Haskell声明样式没有函数的每个参数的规范名称,因为它是立即模式匹配。我想是它权衡利弊的。

更深入,更复杂的类型
即使使用动态语言,我们也在构建类型方面的程序。然而,只是过渡到静态语言可以说是一个降级。我们实际上有研究表明这一点:关于静态与动态类型和生产力的最着名的论文是关于一个非常贫困的静态类型系统。

对于静态类型开始获得回报并帮助我们设计程序,我们需要它们足够强大。一个好的起点是支持我们可能想要设计的所有三种类型。但由于没有语言做得那么好,我们将不得不与其中一些语言达成和解。这类语言包括简单C和版本5之前的Java。

这些语言的好处是越来越多的编程任务被类型映射出来。通过接口,我们知道实现必须满足一定的最小值。对于数据类型,我们知道函数将通过特定情况下的模式匹配来继续。这种方法还提供了更好的机器对代码的理解,允许自动重构可靠地工作。

在帮助我们设计程序的类型方面,下一步是参数化。这是通过Java泛型,C ++模板等来实现的。此功能源于函数式编程语言,最终被移植。

研究人员继续寻找更多类型可以帮助构建程序的方法,Rust的类型系统与线性类型有相似之处,可以管理生命周期。有许多现代语言进行垃圾收集和异常,我们很容易忘记设计的一个重要部分是“我们如何进行资源管理?”和“我们如何处理错误?”等问题。

对依赖类型也有大量积极的研究。我最喜欢这个研究领域的原因是能够从类型中生成实现(或至少部分实现)。如果您从未目睹过这种互动,我建议您观看Edwin Brady关于Idris 2的视频。这为我们今天没有真正体验过的语言提供了一定程度的IDE支持。部分地,这是来自IDE(和语言工具)的这些东西的缺乏良好支持,但部分地,它也是通过(甚至那么复杂的)向语言添加依赖类型而启用的新特征。

虽然我常常纠结于编程人员过分关注函数而不是抽象的属性,但类型系统却会导致人们发生相反情况,人们有时为了正确性过于追求类型:他们过度关注属性。

类型是程序设计的机器可读描述,这很强大。


结束说明

  • 我的意思是今天的文章的主要观点是普遍的:因此我强调类型是设计的核心,即使在动态类型语言中也是如此。但后来我有点嘲笑静态类型的优点。
  • 回想起来,也许我应该说说“duck类型设计美学”。总体思路是支持随意/追溯创造未命名的结构类型。
  • 在我发布后不久的更多“想法”中,类型类可以使用逻辑编程在Haskell中构建实现的方式也很有趣。
  • Edwin Brady 也有一本叫做类型驱动开发的书。我还没有机会阅读它。可能很有趣。

(banq注:类型应该也是针对人类可读性的。类型系统是高阶逻辑,理发师悖论起源于无类型的荒芜时代,差点推翻数学大厦,直到罗素提出类型理论。集合的类型和个体类型是两种不同类型)