哪种编程语言生成最少的难以发现的错误?[关闭]


54

您认为哪种语言可使普通程序员输出具有最少数量的难以发现的错误的功能?当然,这是一个非常广泛的问题,我对非常广泛和笼统的答案和智慧感兴趣。

我个人发现我花很少的时间在Java和C#程序中寻找奇怪的错误,而C ++代码具有其独特的重复性错误集,而Python /类似的语言具有其自己的一组常见和愚蠢的错误,编译器可以检测到它们用其他语言。

同样,我发现在这方面很难考虑函数式语言,因为我从未见过用完全函数式代码编写的大型复杂程序。请输入您的意见。

编辑:完全任意地澄清难以发现的错误:花费15分钟以上才能重现,或者花费1小时以上才能找到原因并修复。

如果这是重复的内容,请原谅我,但是在这个特定主题上我什么都没找到。


10
我希望看到对此主题进行的一些研究!不仅“我的轶事证据表明我唯一知道的语言是国王”,而且大型项目的错误率等等。
Frank Shearar 2010年

@Frank ..如果您有很多时间(我的意思是很多),您可以从ohloh中挖掘一些统计信息,前提是您可以识别修复了数千个代码存储库中的错误的补丁程序。
蒂姆·波斯特

只允许评论的一个。没有其他指示:)
Victor Hurdugaci

4
我认为需要澄清“难以发现”。您的问题和大多数答案似乎将“难以发现”定义为等同于“未被编译器捕获”。您提到python具有可被编译器检测到的愚蠢错误。很公平。但是,这些愚蠢的错误通常并不难找到。当然,它们与C ++错误不在同一类别,C ++错误是由于过早释放内存而产生的。
温斯顿·埃韦特2010年

@温斯顿同意。
Magnus Wolffelt,2010年

Answers:


58

语言的类型系统越强大,在编译时本身就会捕获更多的错误。

下图从类型系统的功能,简单性和安全性方面比较了一些众所周知的编程语言。[ 来源 ]

替代文字

*使用不安全构造的能力的因素。

由于“ unsafe”关键字和关联的指针机制,C#填充到了不安全的行中。但是,如果您想将它们视为一种内联外部函数机制,请随意将C#推向高处​​。

由于功能不安全*系列,我已将Haskell '98标记为纯,但将GHC Haskell标记为不纯。如果禁用不安全*,则相应地将GHC Haskell跳起来。


8
这太棒了!

7
我在图形中找不到Common Lisp:(let ((itbe '())) ... )...
duros

7
什么?PHP的左侧没有剩余空间了吗?
pestaa 2010年

7
C#允许使用安全指针:检查所有指针算法。因此,我相信Java应该支持它。
亚历克斯(Alex)10 Brink 2010年

11
@missingfaktor:我认为C#定位不好。C#允许并推广后继编程风格。不应以它所允许的最糟糕的事情来衡量。
back2dos

20

我认为Haskell可帮助您避免一些常见的错误来源:

  • 它纯粹是功能性的:功能不能有(意外的)副作用,这使多核编程更加容易且不易出错
  • 它是强类型的:您不能例如意外地混合bool,char,int和float值
  • 它是静态类型的:在编译时会捕获许多编程错误
  • null不是值类型定义的一部分:这样可以避免十亿美元的错误
  • 有很多现成的高阶函数可以重用,而不必编写自己的可能有错误的实现
  • 它具有垃圾收集器:几乎消除了内存错误(由于“懒惰”评估策略而导致的“空间泄漏”除外)

11
我不会对此表示反对,但我很想这样做,因为它不符合标准。应该允许“普通程序员以最少的错误计数输出功能”,但是普通的程序员直言不讳地说,它不能使Haskell处于首位。
梅森惠勒2010年

5
优点很多,但是在消除了一些错误类别(意外的副作用,意外的类型转换)的同时,Haskell添加了全新的错误类别:空间泄漏。(尽管没有null,也有undefined,它是每种类型的成员。)
j_random_hacker 2010年

5
@梅森:如果您不写,就不会有错误:)
迈克尔·K

2
我想没有免费的午餐之类的东西-您可以找到易于发现的错误或易于编写的代码,但不能同时使用两种方法:)
Benjol 2010年

6
@Mason什么是“普通程序员”?问题开始于“什么编程语言...”,而不是“ C,C ++和java中的哪个...”;)
Agos 2010年

18

传统上最难发现的错误是多线程应用程序中的竞争条件

  • 几乎不可能复制
  • 可能非常微妙

因此,您需要尽可能多地,无干扰地为您管理并行性的语言。这些还不是主流。Java做了一些,但是让您感到困难。

据我了解,您需要一种功能语言,因为“无副作用”是首先使两个要点消失的东西。我已经看到透明地使Haskell成为高效的多线程语言的工作正在进行,我相信Fortress从一开始就被设计为一种高效的并行语言。


编辑:在Java中Executors处理更多的困难部分。您需要使各个任务符合Callable界面。


5
++比赛条件。你可以再说一遍。
Mike Dunlavey 2010年

是的 请记住,即使有语言支持,并行计算通常也很难。(尽管有很多语言支持,但常见的Lisp宏很难,因为它们的功能非常强大。相同的原理。)
David Thornley 2010年

那Erlang呢?
Malfist 2010年

@Malfist,我对Erlang的了解还不够多。也许您真的想知道一个问题?

2
Erlang是一种功能编程,旨在使多线程变得简单和安全。您不共享变量,而是传递消息。阅读它的维基百科页面。
Malfist 2010年

13

Ada的设计宗旨是尽可能在编译时而不是运行时捕获它。这意味着,用Ada编译程序的时间通常比用Java说的等效时间长10倍,但是当它编译时,您可以更有把握地相信,当程序被编译时,整个bug类都不会显现出来。跑。


7
+1是正确地指出Ada在编译器中捕获其他语言忽略的内容。-1表示断言这意味着Ada程序需要花费10倍的时间才能编译。艾达奖赏那些设计的程序员!!!他的代码,谁在想!!!在开始疯狂打字之前他正在做什么。我的个人经验是,在Ada中为防御工作进行生产编程时,使Ada代码编译所需的时间不会比C / C ++或FORTRAN花费的时间长得多,但是Ada代码以后的麻烦却少得多。普惠公司注意到类似的情况。
约翰·R·斯特罗姆

1
@john r strohm,据我了解,他不是在说编译器编译代码所花费的时间,而是在使代码可编译的时间。
Malfist 2010年

哦,同意让代码可编译。我记得很多程序员在学习有关挑剔的语言时发表的性别歧视言论。通常按照Well的说法,如果您确实以女人的名字命名……
Michael Shaw,2010年

@托勒密,如果可以用女性(显然是美国航母除外)的名字来命名船只,那么编程语言也可以。而是将其命名为“ Ada”而不是“ USS Ronald Reagan” :)

1
一旦我克服了学习难题,在花了很多时间思考设计之后,我实际上就很快地编写了Ada代码。Ada绝对不是黑客的语言。我现在使用Java,在我当前的项目中看到的一些内容实际上使我有点想念Ada(从没想过我会这么说)。
詹姆斯·亚当

7

首先是一个定义:据我所知,一个难以发现的bug是可以重现的bug,但原因很难找到。

可能最重要的方面是我所说的狭窄度,即漏洞可以逃脱多远,漏洞可能影响的范围有多大。在像C这样的语言中,错误(例如,负数组索引或未初始化的指针)实际上会影响整个程序中的所有内容,因此,在最坏的情况下,您必须检查所有内容以查找问题的根源。

在这方面,好的语言支持访问修饰符并以使其难以或无法绕过它们的方式实施它们。好的语言鼓励您限制变量的范围,而不是让全局变量变得太容易(例如,“未明确声明的所有内容都是具有默认类型和值的全局变量”)。

第二个重要方面是并发性。种族条件通常很难复制,因此很难找到。好的语言提供了易于使用的同步机制,并且它们的标准库在必要时是线程安全的。

这已经完成了我的清单;其他类似强类型的内容有助于在编译时捕获错误,但是以后不会很难发现这些错误。

考虑到所有这些,我想说Java和C#以及JVM和.net世界中的许多其他语言都适合避免难以发现的错误。


手动锁定可能看起来是“易于使用的同步机制”,但实际上仅是“易于使用,但是在追逐该死锁时您会感到很开心”。而且,您可以使用多种语言进行基于Actor的并发。
Frank Shearar 2010年

弗兰克:至少锁定是很简单的让每个人都可以做到这一点无需花费几天,搞清楚要使用的API等
user281377

然后,有人认为并发解决方案“足够简单,每个人都可以做到”,就像给学龄前儿童提供了台锯。
David Thornley 2010年

@David:我明白你的意思,但是在很多情况下,一个简单的解决方案实际上就是所需要的。例如,考虑只有少数用户使用的servlet,该servlet必须使用共享资源(例如数据库连接)。如果没有同步,竞赛条件便时有发生。但是,将所有数据库访问操作都放在一个synced(connection){}块中并不是完全科学的选择。
user281377 2010年

+1对于此定义“错误可能影响的范围有多大”。我在使用动态语言时遇到了一些非常讨厌的错误,其中错误数据类型的对象在表现为错误之前就已经在代码中占了很大的距离(这要归功于鸭子的输入)。
Giorgio 2015年

7

由于Excel是使用最广泛的DSL,因此我将继续使用Excel。(当然不包括VBA)

符合要求:

  • 总是很容易复制(这是一个电子表格-不起作用)
  • 很容易找到该错误,因为它完全是“功能性的”-从错误的单元格开始,并追溯其所有依赖关系。

只要您不添加易于发现的错误,这可能是正确的。
mouviciel 2013年

+1有点厚脸皮,因为Excel是数据,而不是语言。给了我一个好笑:)
recursion.ninja

2
@awashburn-哦,我不知道。我认为它有资格作为一种语言。每个单元都是一个“变量”。每个变量都声明性地设置为文字(例如123ABC)或函数(=SUM(A2:A5))。然后,Excel评估所有变量,找出解决依赖关系的顺序等。当然,不仅仅是数据。
Scott Whitlock 2014年

2
我撤回了我的声明,事实证明Excel是Turing Complete ...我学到的东西完全令人不安...
recursion.ninja

1
“ ...真正的[Lisp]大师意识到所有数据都是代码。” 也许足够奇怪,这也适用于Excel。blogs.msdn.com/b/sriram/archive/2006/01/15/lisp-is-sin.aspx
James Mishra

7

这是一个棘手的问题,因为大多数错误不是语言本身的错误-而是开发人员在使用语言时出错的错误。

我认为语言功能有几个方面会影响错误的可能性:

  • 交互性 -具有REPL的动态语言鼓励与正在运行的程序进行交互/进行实验,并缩短代码/测试周期。如果您认为迭代是发现干净的简单解决方案并检测/消除错误的好方法,那么这将倾向于使用交互式语言。

  • 富有表现力 -如果代码较短且样板/偶发性复杂度较低,则更容易发现错误/逻辑错误。

  • 类型安全性 -编译时间检查越多,编译器将捕获更多的错误,因此通常来说类型安全性是一件好事。但是,这些错误通常并不难找到 -即使使用完全动态的语言,数据结构中的错误类型通常也会导致非常明显的运行时错误,而TDD几乎总是会拾取此类错误。

  • 不变性 -许多硬错误是由于可变状态的复杂交互所致。强调不变性的语言(Haskell,Clojure,Erlang)通过避免可变性而具有巨大的优势

  • 功能编程 -编写代码的功能方法比具有复杂效果/交互序列的面向对象的代码更“证明正确”。我的经验是FP可以帮助避免棘手的错误-我相信有些学术研究目前尚无法证明这一点。

  • 并发支持 -并发问题特别难以检测和调试,因此这很重要。任何需要手动锁定的操作最终注定都会失败(这几乎包括了每种面向对象的并发方法)。在这方面,我所知道的最好的语言是Clojure-它具有一种独特的并发管理方法,该方法将软件事务存储与不可变的数据结构结合在一起,以获得新颖,可靠且可组合的并发框架。有关更多信息,请参见http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey


像我这样的热情支持函数式编程的人都喜欢这个答案。富有表现力是你的朋友。
Sridhar Sarnobat

1
到目前为止最好的答案!我认为这是由以下两次错误频率分析所支持的:deliberate-software.com/safety-rank-part-2macbeth.cs.ucdavis.edu/lang_study.pdf。它们都表明,语言越纯净,功能越多,表达能力越强,语言越安全,它所包含的错误就越少。同样,它们都表明Clojure和Ruby逃避了安全规则,这可能表明交互性具有与您所指出的同等的作用。
Didier A.

@DidierA。不幸的是,ucdavis链接已断开(没有wbm存档)-我认为您是从Prem Devanbu的页面上获得的,该页面现在指向更新的链接:Github中的编程语言和代码质量的大规模研究
icc97 '18

5

语言功能越弱,它给您射脚的机会就越少。

与C ++等低级语言相比,Java和C#等高级语言产生的错误更少。

话虽如此,我相信Java比C#更安全。Java是人为限制的,因此没有高级知识的普通程序员可以掌握Java并生成稳定的程序。


4
+1表示“语言的功能越弱,它给自己打脚的选择就越少。”
Michael K

Missingfaktor的图表似乎表明,C#和C ++在能够将自己击倒那里并将Java提升到更高水平方面处于同等的“地位”。不过,我并不是同意图表的那部分。您被迫穿箍进行一些C#射击。
Jesse C. Slicer 2010年

6
安全和功率不是成反比的(这就是您似乎想的;-),例如,Haskell非常强大,但几乎没有办法使自己步履蹒跚。
missingfaktor 2010年

3
如果语言较弱,则只需要更大的一只脚即可。

7
@missingfaktor,这是因为用脚射击自己是一种副作用。

3

您认为哪种语言可使普通程序员输出具有最少数量的难以发现的错误的功能?

我认为,德尔福。基于Pascal,该语言非常简单直观,普通程序员(甚至是经验不足的编码人员)都可以轻松上手,并且其丰富的工具和库支持使大多数错误易于发现。

  • 强类型和严格的编译器可捕获许多常见错误。
  • 不鼓励常见错误的直观语法。(例如,“ The World's Last Bug” if (alert = RED) {LaunchNukes;}将不会编译。)
  • 一个经过精心设计的对象模型,可以消除许多常见的C ++ OOP错误。
  • 语言内置的边界检查和范围检查,大大减少了安全问题的可能性。
  • 可能是人类已知的最快的编译器,它可以提高您的工作效率,并且在等待构建时更难以失去思路。
  • 调试器Visual Studio的调试器希望像它长大后一样。
  • 泄漏跟踪直接内置在内存管理器中,使查找和修复内存泄漏变得微不足道。
  • 大型,成熟的标准库,提供了预构建和预测试的方式来完成常见任务,而无需构建自己的,可能有错误的实现。
  • 随附有用的工具,例如功能强大的日志记录系统和探查器,以使查找问题更加容易。
  • 社区对标准库中未包含常见问题的强大支持,包括强大的第三方并发库

我曾经是一位Delphi骑师,但是当它允许我将任何内容转换为其他内容时,它就不再是Pascal的根源了,例如C / C ++:var I: Integer; Pointer(I)^ := $00;
Jesse C. Slicer 2010年

1
@杰西:也许吧,但我认为这是实用主义的必要让步。当Kernighan撰写《为什么Pascal不是我最喜欢的编程语言》时,他提出了很多优点 类型转换对于完成许多重要的底层工作是必需的。但是,Delphi的优点之一是其库封装了低级详细信息的方式,并使大多数不安全的指针和类型转换变得不必要。
梅森惠勒2010年

我不同意这可能是必要的-但声称强类型打字对此有所否定。最初的Pascal不允许使用这种恶作剧,因此被严格键入。但是我不会这么称呼Delphi弱类型-它有点像“中等类型”。
Jesse C. Slicer 2010年

1
@Jesse:Pascal的原始Wirth版本没有允许变体记录吗?在IIRC中,它们最终成为破坏强类型的常用工具,以至于Borland和其他人决定采用类型转换来简化它,因为每个人都在这样做。
梅森惠勒2010年

en.wikipedia.org/wiki/Pascal_(programming_language)#Divisionsen.wikipedia.org/wiki/Pascal_(programming_language)#Criticism以及pascal-central.com/ppl/chapter3.html似乎表明它是这是1983年的第一个标准。我确实看到了Wirth的一些参考文献,这些参考文献似乎可以追溯到1974年,所以我说是的。我认为有问题的部分是允许这样颠倒(即,变体字段占用相同的内存,就像C中的并集一样)。如果仅将它们用作作用域机制,并且内存布局用于超集,则类型更强。
Jesse C. Slicer 2010年

2

要考虑的一件事是周转时间。

在过去的五年左右的时间里,我主要用Java开发了Web应用程序(JSF,Seam等)。最近我找到了一份新工作,并且我们正在使用Perl(与Catalyst和Moose一起使用)。

我在Perl中比在Java中生产力更高。

原因之一是无需编译和(热)部署。我还发现编写用例更容易,因为可以以更迭代的方式完成。Java框架似乎不必要复杂,至少对于我参与的项目而言。

我猜我的Perl代码中的错误数量与Java代码中的错误数量大致相同,甚至可能更高。但是,我发现更容易,更快地找到并修复这些错误。


1

也许调查每种编程语言可用于静态和动态代码分析的工具的数量可能会有所启发。一种语言使用的工具越多,该语言很可能在用户中非常流行,或者在生成难以发现的错误时非常流行。但我无法让Google指出有关该主题的任何研究。还应注意,某些语言(例如C)可用于解决潜在的硬件错误,以及随着硬件的老化而老化。


1
“随着老化,解决硬件的磨损” ...?
j_random_hacker 2010年

我已经读到一些在关键任务机器上运行的Unix操作系统会检查CPU,RAM和其他硬件的运行状况。 serverfault.com/questions/56192/…对此进行了深入讨论。如果随着时间的流逝,RAM模块中的某些线路出现故障,那么这些故障的模块将不会被操作系统使用,也不会在可用的总物理内存中报告它们。这样的事情也可以在其他硬件上完成。
vpit3833'1

这是一个有趣的花絮,但我在这里看不到它的意义。同样,您的链接中也没有提到这些自我修复的Unix操作系统-只是谈论了对PC硬件进行压力测试的方法。
j_random_hacker 2010年

1
我提到它的意思是仅程序本身可能不是错误的来源,也可能是硬件或其他外部因素。
vpit3833'2

1

与其谈论语言,不如谈论语言功能

  • Java迫使您考虑异常(引发...),并且您必须公开或处理这些异常。这是否真的使我忘记了错误情况,还是我使用了从SystemException派生的不需要此处理的更多异常?
  • 关于“按合同设计”(http://en.wikipedia.org/wiki/Design_by_contract)又如何呢?这迫使我考虑前置条件和后置条件。我已经读过,现在可以使用c#-4.0了。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.