「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」。
前言
本文翻译自一篇外网对Java之父:詹姆斯·高斯林的采访。
介绍
James Gosling,通常被称为“Dr·Java”,是加拿大计算机科学家,最著名的是Java编程语言之父。他做了Java的原始设计,并实现了它的原始编译器和虚拟机。我们的 DevRel,Grigory Petrov,有机会采访了 James,我们在下面附上了完整的采访单。希望你喜欢它!
采访
Grigory:作为软件开发人员和软件顾问,我们正在尝试在俄罗斯组织一个社区:Python、Ruby、Java 和 Go 社区。我们希望通过进行采访来帮助我们的开发人员同行,重点关注我们行业的基本问题。我认为您在 Java 方面的经验和工作可以帮助开发人员变得更好。所以让我们试着帮助他们吧!
某些语言(例如 Go)省略了类和继承,而其他语言则尝试使用 Rust 中的特征等功能。作为语言设计师,您认为编程语言进行组合的现代、通用、合理的方式是什么?
James:我不认为我不会上课。实际上,我发现课程非常适合作曲。对于如何做不同的事情,我真的没有任何好的、清晰的想法。我会以不同的方式做的一些事情有点奇怪。在 C 中有宏,这几乎是一场灾难,因为宏不是语言的一部分;他们有点超出它的范围。 Rust 的人试图在语言中很好地适应宏。
其他语言,如所有 Lisp 家族,设法更优雅地适应它们,但它们有一种定义语法的方法,其中语法几乎完全没有语义。在大多数语言中,语法和语义是齐头并进的。作为过去写过很多 Lisp 的人,我真的沉迷于使用 Lisp 程序来操作 Lisp 程序的技术。这是我非常非常怀念的一件事。有些语言让你以不同的方式做到这一点,所以就像在 Groovy 中一样,你可以直接使用 AST。 Rust 有某种语法集成的宏。但我总觉得那里有一个有趣的研究问题:你能做更多吗?
我能体会到 Lisp 对代码片段进行计算以生成新代码的感觉吗?在 Java 世界中,人们会这样做。这是最流行的功能之一,只是它的级别非常低。因为人们使用注释的组合以及您可以使用某些不同语言生成字节码的事实。那是超级强大的。它可以在您意想不到的地方使用,例如在杰克逊。它通过计算序列化程序获得了很多性能。
一方面,这是一种非常强大的技术。另一方面,它非常难以使用。事实证明这是可能的。但你能走多远?它们可能是有限的。所以,如果你看看像 Lombok 这样的东西,我发现它是其中之一……好吧,我对它有强烈的爱恨交织。因为它添加了一堆非常好的 Java 特性,但另一方面,它也显示出弱点。部分在这个过程中,因为这是一组应该只是内置的功能。 Java Community Process 已经变得比它应有的社区少了一些。这些天我在外面,已经有好几年了,但有些事情你可以做,而且到处都是。
Grigory:这就是为什么我们准备了有关您在创建语言方面的奇妙体验的问题,而不是一些现代 Java 增强提案。五年前,我可以承认,我操纵了一些 Java 字节码。当然,这是好的,但是从中创建特定于域的语言有点棘手。有了 Ruby,事情就简单多了。我们 Evrone 精通 Ruby,我们拥有数十名 Ruby 开发人员。 Ruby 开发人员很棒,但他们需要很多很多年的培训才能学习所有的 DSL 魔法。
James:具有计算代码片段等功能的事情之一,它在 Java 中很尴尬的原因之一是,Java 试图一路编译机器代码。 Ruby 几乎总是被解释的。当你这样做时,当你不试图获得你所能达到的所有表现时,生活就很容易了。但是,如果您想同时获得强大的功能和终极性能,生活就会变得更加艰难。
Grigory:最近,我们采访了 Ruby 的作者 Yukihiro Matsumoto,他提到他已经对他的最新主要 Ruby 3.0 版本进行了实验。他试图在不破坏更改的情况下发布此版本,看看会发生什么。不会破坏任何内容的主要语言版本。我知道 Java 对不破坏事物持谨慎态度。所有语言都在没有不兼容的情况下发展是一个好主意吗?或者它是一种只能用于特定语言(如 Ruby 或 Java)的有限方法?
James:这几乎完全取决于开发者社区的规模。每一次重大更改都会给开发者社区带来痛苦。如果您的开发人员不多,那么破坏性更改不是大问题。而且您还必须考虑成本效益的权衡。如果你做一个突破性的改变,它会增加一些痛苦,但也会带来一些好处。例如,如果您将下标运算符从方括号更改为圆括号,它可能对您没有任何帮助,并且会引起极大的痛苦。那将是一个愚蠢的想法。
在 JDK 9 中,发生了变化,这是引入的极少数破坏性更改之一,它破坏的是:如果您使用一些所谓的隐藏 API,封装机制会被打乱,而那些破坏的人封装边界和使用不应该使用的东西以不应该使用的方式使用,他们在从 8 到 9 的过程中遇到了一些痛苦。但是一旦我们超越了这个界限,它就会让平台有更多的创新自由。而在这种从 8 到 9 过渡的特殊情况下,这意味着平台可以切片和切块,您实际上可以进行自定义包装,这样 Java 运行时环境就会更小。
另一个总是让人感到不舒服的地方是:当某事存在错误,并且人们为该错误制定了变通方法时,如果您修复了该错误,您可能会破坏变通方法。在 Java 世界中肯定有一些例子,我们决定要么不修复错误,要么引入一种做正确事情的方法。这甚至出现在硬件中。 sin 和 cos 有一个问题,它们有点不正确,所以你必须有正确和不正确的说明。
Grigory:二十五年前,当我开始自己的软件开发生涯时,我写了很多 C 和 C++ 代码。我记得这些每月发生一次的神秘指针错误。调试此类错误是一种痛苦。但是现在,作为软件开发人员,我看到许多工具集成到我们的工作流程中,例如静态类型检查器。现代开发人员使用 IDE,如 NetBeans、IntelliJ IDEA,甚至 Visual Studio Code。他们编写源代码,静态类型检查器解析程序,构建抽象语法树,并检查所有可以检查的内容。然后在文本编辑器中突出显示可能的错误。这些技巧不仅适用于静态类型语言,甚至适用于动态类型语言,如 Python、Ruby 和 TypeScript。您对我们今天使用的这些静态类型检查器有何看法?它们是向编写更好的软件迈出的一步,还是我们需要在语言语法中加入更多内容?
James:嗯,两者都有。我是具有静态类型系统的语言的忠实粉丝,因为它们为静态类型检查器和 IDE 提供了一个工作框架。我一生中的大部分时间都是作为一名软件工程师度过的,而对我来说,最不令人满意的消磨时间的方式就是寻找在奇怪的时间发生的晦涩的错误。我可以做的任何事情让错误在它们浪费我的时间之前消失是一件好事。所以,我非常喜欢 IDE 可以做的任何事情来降低错误的可能性。因此,当我们查看 JavaScript 和 Python 等动态类型语言时,他们没有足够的推理框架来解决这个问题,因为他们不一定知道任何东西的类型;他们只是在猜测。强类型语言(如 Java)为类型检查器提供了更严格的框架以供使用。而且,在另一个层次上,有些东西可以进行全自动定理证明。所以有像 Dafny 这样的系统,它有一个非常复杂的定理证明器。因此,如果您想构建加密算法,您将能够在数学上证明属性。你可以这样做。这可能有点过头了,但对于某些代码来说,它确实很有用。
而且很大程度上取决于您的目标是什么。如果你是一名大学生并且你正在努力完成你的作业,或者你是一名博士。学生,并且您正在努力毕业,那么当您编写程序时,您的目标是它应该运行一次。至少一次。因为你必须做一个演示并能够展示它,看看它是否有效。如果你在工业环境中,我一生中的大部分时间都在那里工作,工作一次只是有点用处。它必须每次都有效。一次工作和每次工作之间的区别是巨大的。因此,如果它只需要工作一次,那么更动态的语言工作得相当好。如果您必须确保它会一遍又一遍地工作,那么所有静态类型工具都可以帮助您建立信心。但是,如果您正在做的事情是……比如说,您是一名物理学家,并且想要找出某些计算的结果,那么它只需要运行一次。这取决于您所做工作的背景。您需要的软件可靠性越高,静态类型语言的帮助就越大。
Grigory:谈论企业和工业发展。我自己从来没有给机器人编程过,但我花时间为为数百万人开发软件的公司工作,我可以将今天和 20-25 年前进行比较。我现在看到,像 GitHub 这样的社交编码平台得到了大公司的支持,它们帮助个人开发人员和企业或工业软件开发人员进行开源开发。那么我们今天可以称之为开源软件的黄金时代,还是不是很清楚?你怎么看待这件事?
James:我不知道。你在问一个关于未来的问题。而问题的问题,“今天是黄金时代吗?”……这个问题含蓄地说:“从这里开始走下坡路了吗?”如果这是黄金时代,那么明天就不那么黄金了。而且我认为我们正在引领它,无论黄金时代是什么。我认为可能会发生很多有趣的改进。目前,我们在安全方面存在各种危机,以及人们如何进行网络恐怖主义。当这种事情发生时正在继续,我不认为这是黄金时代。如果人们社区的合作有某种方式可以导致网络恐怖主义的终结 - 那将是非常黄金的。我们会看到。我的意思是,这是一个真是美好的时光,但还可以更好。
Grigory:您使用 JIT(即时编译)创建了 Java 和 JVM(Java 虚拟机)。 JIT 提供了非常惊人的速度,同时保持语言语法愉快和高级。许多语言都跟随您的脚步,例如 C# 和 JavaScript。通过热路径编译和重新编译代码的速度接近 C 和 C++。但是许多其他语言,Python、Ruby、PHP,都有可选的 JIT,但并不那么流行。许多主流语言不使用 JIT 来获得如此巨大的速度提升。为什么不是所有语言都使用 JIT 为软件开发人员提供极好的速度?
James:要真正获得您所看到的性能改进,拥有一种静态类型语言非常有帮助。对于像 Python 这样的动态类型语言,这真的非常困难。通常情况下,人们最终会在语言中添加注释,以便获得类似 TypeScript 的语言,它本质上是带有类型注释的 JavaScript。这真的很有趣,因为 JavaScript 本质上是 Java,删除了类型声明。因此,TypeScript 本质上是具有排列语法的 Java。他们有一些 Pascal 风格的声明。但是,如果您只是在 Python 中编写快速脚本,那么这个世界上的很多人都会发现声明很烦人。考虑变量的类型很烦人。
在 Python 和许多其他人中,一般来说,只有一种数字,那就是双精度浮点数。没有真正的整数,没有字节和 16 位整数以及类似概念上增加复杂性的东西,但它们也提高了性能。如果你有一个双精度浮点数和一个单精度浮点数,那么就会有认知负担。要做出明智的权衡,您必须了解一些数值分析。对于软件工程师来说,对数值分析几乎一无所知是很常见的。所以他们宁愿不去想它。如果您是一名使用 Python 的物理学家,您可能希望获得几乎总是可以获得的所有精度。当然,除非您需要在内存中放入一个非常大的数组,其中单精度和双精度或 8 位整数之间的区别真的很重要。如果你生活在一个这些事情都没有任何后果的空间,那么对人们来说会更容易。
但是如果你需要关心的话……我这辈子上过太多的数值分析课程,并且被劣质的数值分析烧伤了足够多的时间,我倾向于关心。这取决于您在频谱上的位置,脚本语言世界中的大多数人并不关心这类问题。他们的担忧处于非常不同的水平。很多人实际上并不关心性能和数字的细节;他们关心的是:“速度够快吗?”性能是一种布尔值:它足够快,或者它不够快。对于某些人来说,这更像是调整赛车。如果您的汽车每小时能多跑两三英里,那么您就更有可能赢得比赛。
Grigory:我记得几个月前,Ruby on Rails(广受欢迎的网络框架之一)的作者 David Heinemeier Hansson 提到,他的云预算中只有 15% 用于语言本身。其余的是一些缓存、消息队列、存储等。他告诉我们,无论 Ruby 有多“慢”,它都不是很重要,因为即使 Ruby 快了 100 倍并且 15% 变成了 1%,这也不会发生太大变化。现代语言确实“足够快”。
James:这在很大程度上取决于您的任务在程序空间中的哪个位置。如果你想要完成的事情真的是由网络和数据库以及其他所有东西主导的,如果你一直在做 RPC,那么你应该做的第一件事可能是问是否所有这些 RPC 都是有价值的。当人们谈论微服务时,它们是一件好事,但只要明白它们至少比方法调用慢一百万倍。仔细想想这意味着什么。通常,对于大多数人来说,通过确保他们的大规模架构是干净的,他们会获得更多的性能。但是对于很多人来说,所有低级细节真的很重要。如果您知道高度并发很重要,能够同时驱动数千个进程,进行主要计算……如果您正在做诸如数据库本身或主要存储服务之类的事情,那么您真的非常关心。所以这一切都取决于手头的任务。
Grigory:最近,我们看到许多语言都采用协程和异步/等待方法来处理网络等很慢的事情。它被添加到 Python 中,它被添加到最近的 Ruby、JavaScript、许多语言中。但是在一个线程中的这种异步/等待和协程和调度程序并不是万能的。它们带来了自己的复杂性,有时它们会使软件变慢。那么您如何看待这种现代的异步/等待炒作?这是处理网络的好方法,还是我们只是滥用它,我们需要检查 Erlang 和其他方法?
James:这是上下文决定一切的事情之一。协程非常好;它们从 60 年代就存在了。第一种带有协程的语言是 Simula 67。Simula 是一种可爱的语言。我仍然很想念它。它没有线程,它有协程,但是它们执行协程的方式 - 它们看起来很像线程。协程有点神奇地回避了真正并行中的一些顽皮问题。对我来说,协同程序的问题之一,这就是我很久没有使用它们的原因,是它们实际上并没有让你使用或让你利用多个处理器。你不能做真正的并行。
所以人们会用具有真正并行性的语言来看待事物,比如 Erlang 和 Java。您必须做的事情又增加了一个复杂度。尽管通常情况下,您处理这种复杂性的方式是通过精心策划的原语。你可以用 Java 中的 ConcurrentHashMap 做的事情很神奇。但是,一旦您拥有这些基于协程的语言中的一种并且您尝试利用多个处理器,如果您正在执行大量协程类型的操作而您没有足够的处理器,那么您只会使一个处理器饱和处理器。你真的很想使用多个处理器,因为世界上不再有单位处理器了,对吧?一切都有很多内核,如果你真的想一次使用你所有的计算机,在一个问题上,你只需要解决和处理真正多线程中固有的复杂性。
然后是风格问题。想象一下这样的环境,你可以说“等待这个”和“等待那个”,他们会在你被动让步的情况下进行这种透明的控制反转。这为您提供了一种看起来很像真正线程的语法外观。但这意味着您可以避免真正线程中的许多棘手问题。因此,如果您说“a = a + 1”,您就知道在该操作的中间您不会被中断,因此您不必进行同步。但是在其他地方,它不是做那种风格,而是成为一种事件导向的风格,在那里你做你的事情,然后你将一个事件处理程序插入一些东西来处理当事情完成时发生的事情。这往往是 JavaScript 中的主要风格。这工作得很好,但它可能会变得有点笨拙。
当我在 70 年代初发现 Simula 时,它有一种自然的风格。你只是编程,你可以把你的计算看作是一个独立的东西。其他事物是否与它交织对您来说是透明的。我发现它作为一个概念模型,比事件编程要简洁得多。在幕后实施更难,但通常更容易考虑。
Grigory:毕竟 Simula 是第一个面向对象的语言!我从来没有机会使用它,但我查看了文档,它看起来很有特色。然而,如果我们回顾一些现代语言,如 Ruby,并发模型是复杂的:我们有进程、进程中的单独解释器、单独解释器中的线程以及线程中的核心例程——就像一个俄罗斯娃娃。如果您允许,现在是一个非技术问题。当我们谈论不同的语言时,在您个人看来,现在教新软件开发人员的最佳语言是什么,作为他们的第一语言?也许在研究生院或大学。
James:我显然偏见了。 Java已经使用了很长时间的那种方式。但我学习的第一个编程语言是PDP-8汇编代码,并随后与Fortran粗略同时。你可以教人们对任何事情。它会比其他人更容易到一些人,但很多取决于一个人的最终职业道路将是什么。如果您将成为一个完整的软件开发人员,您正在构建种类的大型高性能系统,很难击败在JVM上运行的任何语言。而且我实际上不关心你在JVM上使用的语言。我的意思是,Scala和Kotlin都很好。 Clojure真的是娱乐,但你必须真正思考。如果你是物理学生,Python很好。
而且我认为这实际上是你选择的交易中的大量交易。虽然很多人只是坚持他们学到的第一件事,如果你能让人们要学习多种语言并来回走......每个大学都应该为每个学生运作的真正愉快的课程是一个比较编程语言课程。在学期中,您有五种不同的计划语言有五个作业,从而让人们常常快速学习它们,因为他们真的不是那么不同,并让他们思考哪些更好。我很久以前就拍了其中一个课程,我将每次分配使用绝对最差的语言。在COBOL中进行数值计算。这只是娱乐!在Fortran中象征性的操作......令人惊讶的是,我还有一个A。
Grigory:正如预期的那样。因此,下一个问题是关于模式匹配。最近,它在Python和Ruby中揭示了它,许多提案有不同的语言。我们检查了开发人员白皮书,他们并不完全确定模式匹配在现代高级语言中的作用。这个模式匹配的想法,你认为它适合普通的现代开发人员的工具包,他们使用Java或Python或Ruby或一些高级语言?我们真的需要模式匹配,或者是否有一些利基语法对于非常具体的用例?
James:对于初学者,我认为编程语言中的“模式匹配”一词有点误导。因为当我听到“模式匹配”短语时,斯普林斯到我的思想是正则表达式,无论是对角色字符串的正则表达还是在树上的正则表达式。也许在树形上的模式匹配,无论如何。但回到Simula。 Simula有一个签约声明,签约声明几乎是许多这些模式匹配陈述的陈述。即,inspect陈述是一个案例声明,案件标签是类型名称,所以你会说:
Inspect P
When Image do Show;
When Vector do Draw;
因此,您可以将其视为一种案例的案例。这些模式的大多数匹配语言提案更像是那种类型的东西。就个人而言,我想念那个。我真的很喜欢。特别是如果发生了什么样在C中的隐式展示。所以如果你说“检查p do p do p do p”,p,在案例语句的正文中,现在是交换机标签的类型。这使生活变得更加容易。所有这些地方都在其中,以一种类似的语言,你的语法,你最终一直延伸。它看起来像:“如果一个是x的实例,否则如果a是y的实例,那么......”和simula中的“检查”语句很漂亮;我爱它。从那时起,我每天都错过了它,而且许多这些模式匹配的提案和语言特征看起来像那样。如果你称之为“类型案例” - 好主意。但是,如果你称之为“模式匹配”,它的力量比正则表达更少,而且感觉误导或类似于虚假的广告。但是,作为一个特征,我认为这很棒。
Grigory:我们的最后一个问题有点强制性。俄罗斯软件开发人员为Jetbrains和Kotlin开发感到自豪。当然,我不会问像Java与Kotlin等琐碎的东西等。我会试着问一些不同的东西。 Kotlin和许多其他语言,如Clojure或Scala,茁壮成长,您创建的现有Java虚拟机以及图书馆,框架和现有代码的现有生态系统。是否面临所有这些语言的挑战?有什么可以联合的东西吗?他们有些困难?当他们尝试用一些不同的语法进行热插拔Java语法时,他们会面临什么挑战?
James:有点取决于你想要做的事情。 Java虚拟机的一个特点是,内置了许多安全性和可靠性的概念。他们必须主要用内存模型的完整性做到。指针和类似的东西。所以你不能伪造指针。如果您看起来像C这样的语言,如果您没有伪造的能力,则无法执行C.其中有一些虚拟机在那里他们没有安全模型。在像JVM这样的东西上,如果你试图实施c - 而有些人已经完成了它,虽然它是奇怪的 - 如果你有一个严格的安全虚拟机,你就无法去。但有些人建立了没有严格安全的虚拟机,没有内存分配模型。如果您想在C和Kotlin之间进行互操作性,您必须愿意放弃一定的安全性和可靠性。
这取决于你愿意去的地方。当然,在Java的曙光,我的个人规则之一是:我不想调试另一个怪异的内存损坏错误。我在遮挡记忆腐败虫子的情况下浪费了太多的生活,让你几天。它只是一个刚刚发生的循环中的一个错误,刚刚发生在阵列的末尾的一个条目,并且稍后将找出百万条令。而且我真的,真的很讨厌追逐记忆腐败错误。所以这取决于你对你的舒适。有些人,你知道,认为花时间做的是非常勇敢的。但是还有人喜欢使用vi,这是在80年代和80年代的好的编辑中是一个伟大的编辑......来吧,兄弟们!
Grigory:内存安全模型确实在核心中,它提供了一些东西,但限制了其他东西。谢谢你,詹姆斯!很高兴与您进行这种谈话,我希望毕竟这是僵尸启示录,我们将在一些离线会议上互相见面。谢谢你,祝您有美好的一天!
总结
我们非常感谢与詹姆斯交谈并获得我们每天使用的语言,功能和解决方案的洞察力。
此外,我们希望从Jetbrains到Jetbrains的同事们对我们的同事感到欣慰,以协助采访的文本版本。