这些现代编程语言会让您受苦

2020-12-07 22:45:53

特定编程语言的优缺点是什么? X是我的任务很好的语言吗?搜寻“最佳编程语言”将为您提供“ Python,Java,JavaScript,C#,C ++,PHP”的标准列表,以及优缺点的模糊列表。看到这样的文章会让我感到痛苦,他们的作者一定是完全懒惰,缺乏经验,同时又缺乏想象力。让我们深入研究一下,找出真正的缺点,而不是什么。

在本文中,我将尝试对流行的(而不是那么流行的)现代编程语言给出客观而公正的概述,从最坏到最好的排名。

请记住,没有一种编程语言可以完美地适合所有可能的用例。一些语言最适合于前端开发,其他语言最适合于后端/ API开发,其他语言则非常适合系统编程。

我将介绍世界上两个最常见的语言家族-语言是C衍生的,而语言是ML衍生的。

编程语言只是开发人员工具箱中的工具。选择正确的工具来完成这项工作很重要。我真的希望本指南将帮助您选择最适合您任务的编程语言。做出正确的选择可以节省您数月(甚至数年)的开发工作。

大多数其他类似的文章都根据受欢迎程度和潜在收入等因素进行比较。流行性很少是一个好的衡量标准,尤其是在软件领域(尽管大型社区和生态系统会有所帮助)。相反,我将考虑特定语言的优点和缺点。

我将使用大拇指👍(即+1),大拇指朝下👎或OK👌(无论好坏)表情符号来表示特定语言特征的得分。

现在,我们将如何衡量?换句话说,除了语言流行之外,真正重要的是什么?

许多人按类型系统发誓。因此,近年来诸如TypeScript之类的语言开始流行。我倾向于同意,类型系统消除了程序中的大量错误,并使重构更加容易。但是,“拥有”类型系统只是故事的一部分。

如果语言具有类型系统,那么进行类型推断也非常有用。最好的类型系统能够推断大多数类型,而无需显式注释功能签名。不幸的是,大多数编程语言仅提供基本的类型推断。

对于类型系统来说,支持代数数据类型也很好(稍后会详细介绍)。

最强大的类型系统支持高级类型,这是泛型之上的抽象级别,并允许我们以更高的抽象级别进行编程。

我们还必须记住,人们往往对类型系统过于重视。有些事情远比静态类型重要,选择类型时,类型系统的存在与否并不是唯一的因素。

我们可能拥有完美的编程语言,但是如果新入职的开发人员可能要花费数月甚至数年(前期投资),它将有什么用?另一方面,某些编程范例需要花费数年才能变得精通。

一门好的语言应该适合初学者,并且不需要花几年的时间就能掌握。

我称之为我的十亿美元错误。它是1965年创建的空引用。当时,我正在设计第一个全面的类型系统,用于面向对象的语言中的引用。我的目标是确保对引用的所有使用都绝对安全,并由编译器自动执行检查。但是我忍不住要插入空引用的诱惑,仅仅是因为它很容易实现。这导致了无数错误,漏洞和系统崩溃,在最近四十年中可能造成十亿美元的痛苦和破坏。

为什么空引用不好?空引用中断类型系统。当null为默认值时,我们将不再依赖编译器来检查代码的有效性。任何可为空的值都是等待爆炸的炸弹。如果我们尝试使用我们不认为可能为空但实际上为空的值怎么办?我们得到一个运行时异常。

我们必须依靠手动运行时检查来确保所处理的值不为null。即使使用静态类型的语言,空引用也剥夺了类型系统的许多好处。

实际上,这种运行时检查(有时称为空防护)是针对不良语言设计的解决方法。他们用样板乱码。最糟糕的是,我们无法保证不会忘记检查null。

用一种好的语言,应该在编译时检查值的缺失或存在。

捕获异常是处理错误的一种坏方法。抛出异常是可以的,但仅在例外情况下,程序无法恢复并且必须崩溃。就像空值一样,异常会破坏类型系统。

当使用异常作为错误处理的主要方式时,就不可能知道函数是返回期望值还是崩溃。引发异常的函数也是不可能组成的。

显然,仅仅由于我们无法获取某些数据而导致整个应用程序崩溃是不可能的。但这确实比我们想要的更经常发生。

一种选择是手动检查引发的异常,但是这种方法很脆弱(我们可能会忘记检查异常),并且会增加很多噪音:

如今,错误处理机制更加完善,可能的错误应在编译时进行类型检查。默认情况下不使用例外的语言将排名更高。

我们已经到了摩尔定律的尽头,处理器将不会更快。我们生活在多核CPU时代,实际上,任何现代应用程序都必须利用多核。

不幸的是,当今使用的大多数编程语言都是在单核计算时代设计的,根本不具备可在多核上有效运行的功能。

帮助并发的库是事后的想法,它们只是向最初不是为并发设计的语言添加了创可贴。这并不是真正的良好开发经验。在现代语言中,必须内置并发支持(请考虑使用Go / Erlang / Elixir)。

我认为,当您构建可变对象的大对象图时,大型面向对象的程序会越来越复杂。您知道,尝试理解并牢记当您调用方法时会发生什么以及副作用是什么。

如今,使用不变的值进行编程变得越来越流行。甚至像React这样的现代UI库也打算与不可变值一起使用。具有对不可变数据值的一流支持的语言将排名更高。仅仅因为不变性消除了我们代码中的一整类错误。

什么是不可变状态?简而言之,它是不变的数据。就像大多数编程语言中的字符串一样。例如,大写字符串永远不会更改原始字符串-始终会返回一个新字符串。

不变性进一步推动了这一想法,并确保一切都没有改变。总会返回一个新数组,而不是更改原始数组。更新用户名?将返回一个新的用户对象,并更新其名称,同时保留原始对象。

处于不可变状态时,不会共享任何内容,因此我们不再需要担心线程安全性的复杂性。不变性使我们的代码易于并行化。

不变异(改变)任何状态的函数称为纯函数,它们很容易测试和推理。使用纯函数时,我们不必担心函数之外的任何事情。只需专注于您正在使用的这一功能,而忽略其他所有功能。您可能可以想象,开发变得多么容易(与OOP相比,必须牢记整个对象图)。

语言可能不是很好,但是它可能具有庞大的生态系统,因此很有吸引力。访问优质的图书馆可以节省一个月(甚至几年)的开发工作。

语言的编译速度如何?程序启动速度有多快?运行时性能如何?所有这些都很重要,并将列入排名。

尽管有一些例外,但是一般而言,较新的语言会比较旧的语言更好。仅仅因为更新的语言可以从其前辈的错误中学习。

让我们从最坏的情况入手,这可能是计算机科学中最大的错误之一,C ++。是的,C ++不被认为是一种崭新的现代编程语言。但是今天它仍在广泛使用,必须将其包括在列表中。

C ++是一种可怕的语言…而且将您的项目限制为C意味着人们不会用任何愚蠢的“对象模型” c& @p弄乱事情。 — Linux的创建者Linus Torvalds。

C ++具有许多功能。它尝试做所有事情,同时又不擅长任何特定的事情。 C ++具有goto,指针,引用,OOP,运算符重载和许多其他非生产性功能。

为什么C ++如此糟糕?我认为,最大的原因是它的年龄。 C ++是在1979年设计的很早的时候。当时的设计师缺乏经验,也不知道该关注什么。当时添加的功能似乎是个好主意。该语言非常流行,这意味着添加了许多功能来支持各种用例(创建更大的功能混乱)。

C ++因其编译时间慢而臭名昭著。比Java慢得多,不如Scala差。

我曾希望可以选择启用的垃圾收集器将成为C ++ 0x的一部分,但是存在足够的技术问题……

垃圾回收从未添加到C ++中。手动内存管理极易出错。开发人员必须担心手动释放和分配内存。我将永远不会错过使用非垃圾收集语言的日子,如今在垃圾收集语言中很容易避免许多错误。

我发明了术语“面向对象”,我可以告诉你我没有C ++。

OOP出现于60年代后期,是C ++工作开始时的一项很酷的新技术。不幸的是,C ++在实现OOP时犯了一些关键错误(与Smalltalk等语言不同),这使一个好主意变成了一场噩梦。

与Java相比,C ++的一件好事是C ++中的OOP至少是可选的。

C ++是一种复杂的低级语言,没有自动内存管理。由于其功能膨胀,初学者不得不花很多时间学习该语言。

C ++是在单核计算时代设计的,仅在过去十年中添加了基本的并发机制。

C ++的最佳用途可能是系统编程。但是,鉴于存在更好,更现代的替代方案(Rust and Go),C ++甚至不应该用于此。我认为C ++毫无优势,请随时证明我是错的。

自1995年首次出现以来,Java比C ++年龄小16岁。 Java是一种简单得多的语言,这可能有助于其流行。

Java提供的优于C ++的最大好处之一是垃圾收集,垃圾收集本身消除了许多错误。

Java已经存在了很长时间,并且它具有一个庞大的用于后端开发的生态系统,从而大大减少了开发工作。

在这里,我不会深入探讨OOP的弊端,有关更详细的分析,您可以阅读我的其他文章《面向对象编程—万亿美元灾难》。

相反,我只是引用一些计算机科学领域最杰出的人,以得到他们对OOP的看法:

很抱歉,我很久以前就为该主题创造了“对象”一词,因为它使许多人专注于较小的想法。大想法是消息传递。 -OOP的发明者艾伦·凯(Alan Kay)

艾伦·凯(Alan Kay)是对的,主流的OOP语言专注于错误的事物(类和对象),而忽略了消息传递。值得庆幸的是,现代语言正确地实现了这个想法(Erlang / Elixir)。

使用受OOP影响的编程语言,计算机软件变得更冗长,可读性更差,描述性更强,更难修改和维护。

使用过OOP语言(例如Java或C#)并具有使用非OOP语言的经验的任何人都可以联系。

显然,Java在Java虚拟机之上运行,而Java虚拟机的启动时间很慢。我已经看到,在JVM上运行的程序需要30秒甚至更长的时间才能启动,这对于现代的云原生程序来说是不可接受的。

在较大的项目上,编译速度很慢,这极大地影响了开发人员的生产力(尽管没有Scala那样糟糕)。

尽管Java是一种相当简单的语言,但它对面向对象编程的关注使真正变得很难。可以轻松编写一个简单的程序。但是,知道如何编写可靠且可维护的面向对象代码可能要花十年的时间。

Java是在单核计算时代设计的,并且像C ++一样,仅具有基本的并发支持。

Java出现时是一种不错的语言。太糟糕了,Java(与Scala不同)一直只专注于OOP。该语言非常冗长,并且受样板代码的影响很大。

从根本上讲,C#和Java之间几乎没有什么区别(因为C#的早期版本实际上是Java的Microsoft实现)。

C#与Java共享大多数缺点。 C#于2000年首次出现,但比Java年龄小5岁,并且已经从Java的错误中学到了一些东西。

C#语法一直领先于Java。 C#比样板代码遭受的痛苦更少。尽管C#是一种OOP语言,但它在冗长的方面更为重要。很高兴看到C#语法在每个版本中都得到了改进,并增加了诸如表达式强健的函数成员,模式匹配,元组等功能。

就像Java一样,C#主要关注OOP。再一次,我不会在这里花太多时间试图说服OOP的缺点,我只想引用一些计算机科学领域的杰出人物。

我认为缺乏可重用性的是面向对象的语言,而不是功能语言。因为面向对象语言的问题在于它们拥有了它们所伴随的所有隐式环境。您想要香蕉,但是得到的是一只大猩猩,拿着香蕉和整个丛林。

我必须同意Joe Armstrong的观点,与功能(甚至命令性)代码相比,重用面向对象的代码非常困难。

提供面向对象的程序作为正确程序的替代方案……—计算机科学先驱Edsger W. Dijkstra

在我整个职业生涯中都使用过OOP和非OOP语言之后,我不得不同意与非OOP代码相比,OOP代码更难正确。

C#声称是一种多范式语言。特别是,C#声称支持功能编程。我必须不同意,仅仅支持一流的功能还不足以使一种语言称为功能。

语言应具有哪些功能?至少,对不可变数据结构,模式匹配,函数组合的管道运算符,代数数据类型的内置支持。

C#是在单核计算时代创建的,就像Java一样,它仅具有基本的并发支持。

我在C#的职业生涯中花费了大量时间,并且总是对这种语言感到沮丧。与Java一样,我建议您寻找更现代的替代方案。它是相同的Java,但语法更现代。

自1991年首次出现以来,Python是一门古老的语言。与JavaScript一起,Python是世界上最受欢迎的语言之一。

Python几乎有任何库。与JavaScript不同,Python无法用于前端Web开发,但是Python可以轻松地构成大量的数据科学库。

Python是一种非常简单的语言,初学者可以在几周内掌握它们。

Python是动态类型的,关于类型系统没有太多要说的了。

Python是一种解释型语言,以运行时性能而言,它是最慢的编程语言之一而臭名昭著。在运行时性能至关重要的情况下,使用Cython代替普通的Python可能是一个很好的解决方案。

在将Python和其他现代语言一起使用后,很难不对Python的依赖项管理感到失望。点子,pipenv,virtualenv,点子冻结等。相比之下,JavaScript中的NPM是您唯一需要的工具。

真的很不幸,Python没有对函数式编程的适当支持。函数式编程非常适合于数据科学试图解决的问题。即使对于诸如网络抓取之类的非常Python的任务,功能性语言(例如Elixir)也更合适。

我不建议将Python用于大型项目,因为该语言在构建时并未考虑到严格的软件工程。

当没有其他替代方法可用时,Python不应用于数据科学以外的其他任何用途。在数据科学领域,Julia似乎是Python的一个很好的现代替代品,尽管它的生态系统还不如Python成熟。

Rust从一开始就被设计为快速。 Rust程序的编译比Go程序的编译花费更长的时间。 Rust程序的运行时性能比Go快一点。

我们列表中的第一种语言,带有现代null替代选项! Rust没有null或nil值,Rust开发人员改用Option Pattern。

Rust采用了​​一种现代的功能性方法来处理错误,并使用专用的Result类型来表示可能失败的操作。它与上面的选项非常相似,但是None情况现在也有一个值。

Rust是我们列表中唯一没有垃圾回收的现代语言。这迫使开发人员考虑进行低级内存管理,并使开发人员的工作效率下降。

由于缺乏垃圾收集,因此在Rust中并发非常困难。开发人员必须担心诸如装箱和固定之类的事情,这些事情通常会以垃圾回收语言自动完成。

作为一种低级语言,Rust中的开发人员生产力无法达到其他高级语言中的水平。这也使学习努力变得更加困难。

Rust非常适合系统编程。尽管它比Go更复杂,但它提供了强大的类型系统。 Rust为空值提供了一种现代的替代方法,并提供了一种处理错误的现代方法。

为什么Rust仍然排名低于TypeScript和JavaScript?它是为系统编程设计的低级语言。 Rust并非非常适合后端/ Web API开发。它缺少垃圾收集,并且不内置对不变性的支持。

TypeScript是一种js编译语言。它的主要目标是通过向JavaScript添加静态类型来制作“更好的JavaScript”。就像JavaScript一样,TypeScript被用于前端和后端开发。

TypeScript是由设计C#的同一人Anders Hejlsberg设计的。 TypeScript代码感觉非常C尖锐,从根本上讲,可以将其视为浏览器的C#。

是的,JavaScript的超集对TypeScript的采用起到了很大的帮助。毕竟,很多人已经知道JavaScript。

但是,成为JavaScript的超集更像是博士

......