编程之魂之C# – 与C#之父Anders的访谈

作者: geekzl管理员 发布时间: 2021-03-22 00:03:16 497 人阅读   百度未收录

编程之魂之C#

与Anders谈C#

《编程之魂》一书第13章

第13章 C#

当微软刚刚处理完来自 Sun Microsystems公司关于Java编程语言修改的诉讼官司,他们就转而求助于经验丰富的语言设计者 Anders Hejlsberg,请他设计一种有强大的虚拟机支持的面向对象的新语言。结果就产生了C#, 用以替代Microsoft 生态系下的Visua C++和 Visual Basic。虽然仍然难免要与Java在语法、实现和语义方面比较一番,但是这种语言本身的发展已经超越了其根源,同时吸收了来自 Haskell 和ML之类的函数式语言的特性。

13.1 语言和设计

Language and Design

您已经创建了并维护着好几种语言。您是从Turbo Pascal的实现者起步的,从实现者到设计者是不是一个自然的过程呢?

Anders Hejlsberg: 我认为这是一个非常自然的过程。 我编写的第一个编译器是为了Pascal的一个子集,然后,Turbo Pascal就是Pascal的第一个近乎完全(complete)的实现。不过,Pascal一直是作为一 种教学语言,而且它缺乏大量的编写真实世界的应用程序所必需的相当普遍的特性。为了实现商业的可行性,我们不得不立即尝试用多种方式来扩展研究范围。

令人惊叹的是一种教学语言能在教学和商业之间牵线搭桥方面如此成功。

Anders: 有很多不同的教学语言。如果你看一看Niklaus Wirth的历史 - Niklaus Wirth设计出了Pascal语言,随后又设计出了Modula和Oberon-他一贯重视简单性。教学语言可以用来进行语言教学,因为它们擅长于教授一个特定的概念,但它们并不是真实存在的、真正能够作为你编程基础的成熟语言。这一直都是Pascal的作用所在。

学校也似乎分为两种流派。有些学校是以Scheme开始的,例如麻省理工学院。而其他学校则似乎侧重于“实际”。有一阵子,他们教的C++。现在教的是Java,有些时候也用C#。您怎么看?

Anders: 毫无疑问,我一直是“实践”派。我是一名科学家,但我更是一位工程师,如果你愿意这么分的话。我的理念是,如果你教人东西,那就教他们一些以后实际用得着的东西。

一如既往,这种回答也不走极端。答案就在二者之间。在编程语言不断的实践中,在行业的编程语言实现中,我们借鉴了学术界的观点。现在,我们看到了得益于函数式编程思想的巨大收获,而这些思想到底已经在学术界存在了多久,也许只有上帝知道。我认为这里的神奇之处在于你必须二者兼顾。

您的语言设计哲学是出自自己的观点并付诸实践的吗?

Anders:噢,在某种意义上是这样的。我认为你可能会,从某些指导原则开始。简易性始终是一个很好的指导原则。另外,我是一个十足的进化论迷,而反对凡事从零开始。

你可能会痴迷于一个特定的想法,于是,为了实现它,你去创造一个全新的语言,而这种语言的妙处就在于它是一个新东西。然后,顺便说一句,90%语言都有某些差劲的东西。如果你还能够不断改进现有的语言,其中有许多类似的东西,例如,最近我们确实对C#:朝着函数式编程方向做了很多改进。我觉得,这些都是意外所得。你有一个庞大的用户基础,它们恰好非常熟悉这些东西。这样会有一点复杂,但是毫无疑问,它要比为了追求某种特定的编程风格而必须学习全新的语言和执行环境要好得多。

要在语言本身及其生态系统之间划出一条界线,这是很难的。

Anders: 嗯,是的,最近无疑是越来越是如此。如果回到二三十年前,你可以说语言支配着你的经验曲线。学习一种编程环境几乎就是学习这种语言的一切。然后,这种语言有一个小的运行时库。如果你能学到操作系统那里的话,操作系统可能还有一些内容。现在你看看我们所拥有的这些庞大的框架,比如说.NET或Java, 这些编程环境是由规模宏大的框架API所统治,以至于语言本身几乎成了事后才考虑的东西。这不完全正确,不过可以肯定的是,它更与环境而不是语言及其句法相关。

这会让库设计者的工作更加重要吗?

Anders:平台设计人员的工作变得非常重要,因为,你确实能最大程度地利用的就是你能否确保该平台的寿命以及在该平台上实现多种语言的能力,而这些则是我们一直看好的。.NET是作为多语言平台开始设计的,而且,你看它现在驻留了各种不同语言: 静态语言、动态语言、函数式语言,还有XAML这样的说明性语言等。然而,它们下面都是相同的框架、相同的API,而这种影响是非常巨大的。如果这些都自成一体的话,你就会在互操作和资源消耗中慢慢耗死。

一般来说,您赞成使用多种语言的虚拟机吗?

Anders: 我认为必须这样。依我看来,它就像你回到了8位系统那段美好的旧时光,你使用的是64KB内存。这些64KB内存会被填满,而且是很快就被填满。这不可能让你去构建可供多年使用的系统。64KB内存,您可以先实现一两个月,也许6个月,你就要用到640KB。现在,它基本上是一个无底洞。用户的需求越来越多,而且我们也没办法全部重写。这就是要创建并利用互操作性。否则,你就永远只是做这种枯燥无味的事,只是在做最基础的工作。如果你能给它们提供一一个共同的基础,并在共享系统服务之外获得更高程度的互操作性和效率,那么,这就是一种方向。以托管代码与非托管代码的互操作性;为例。它会遇到各种各样的挑战。但是,这样远比在每一个不同的编程环境来努力解决它要好得多。最具挑战性的是构建混合应用程序,它一半是托管的,另一半是非托管的,而且您需要在这一半进行碎片收集,而另一半则根本不需要。

在JVM的设计目标中,似乎从来没有准备要破坏向下兼容早期的字节码版本。这就限制了他们的设计决策。他们可以在语言级别进行决策设计,但在实际的泛型实现中,举个例子,他们不得不进行类型擦除。

Anders:你知道吗?我认为,他们的设计目标不仅是向下兼容。您可以添加新的字节码,并且仍然向下兼容。他们的设计目标是根本不动任何字节码和虚拟机。这有很大的不同。实际上,设计目标没有变化。这就把你完全限制住了。在.NET中,我们有向下兼容的设计目标,所以增加了新功能、新的元数据信息。.NET 2.0会有一些新指令、新库等,但.NET 1.0的每一个API都能在.NET 2.0继续运行。

我一直很困惑他们为什么选择了这条道路。我可以理解它为什么让你这样做,但是如果你从本行业的历史来看,一切都在发展变化。一旦你停下了发展的脚步,就等于是给自己判了死刑。这只是一个时间问题。

我们选择的是具体化泛型而不是擦除,对于这种选择,我极为满意,而且获益良多。我认为,我们用LINQ完成的所有工作离开具体化泛型几乎是不可能的。我们用ASP .NET做的所有的动态的东西,在几乎每一个实际产品中生成的所有动态代码之所以能够如此深入,都是受益于这个事实: 泛型是真正地在运行时表示,而且在编译时间和运行时环境之间存在着对称关系。这就是如此重要。

对Delphi的批评之一是,它非常不愿意破坏代码(break code),这影响了一些语言决策。

Anders: 让我们再退一步。当你说到break code时,你必须首先谈谈它的发展。你在谈论它的N+1版。你应当承认,有时中断(打破)代码是件好事,但总体上概括来说,我就不能为中断找到什么托辞了。因为他们并没有很好的理由,我所听到的支持中断(的唯一理由是:“这是更整洁的方式”、“它在结构,上很完美”,或者是“它会给我们的未来作好准备,我们的未来”等。那么我说,你知道,也许平台只能生存10到15年,随后它们就会由于这样或者那样的原因而不堪来自自己的重负。

他们早晚会成为遗留系统,或许需要20年。到那时,它们周围就会出现足够多的新东西,还有很多的新东西不需要任何开销。如果你想要去中断它,那就好好去中断吧。中断一切。要接触最前沿的技术。而不要四处分散精力,那样毫无意义。

这听起来好像是一种需要5年或10年轮换一次的跳背游戏(game of leapfrog)。

Anders: 你要么玩跳背游戏(译注1),要么就好好地把握向下兼容性,并且每次都要带上你的整个团队。

托管代码只能在一定程度上起作用。你可以在这个过程中使用已有的组件。

Anders:当然,从.NET一开始,我们就已经在每一次发布中都保持向下兼容。我们修正了一些造成代码中断的bug,但我的意思是必须有一些定义,通过这些定义中断人们的代码是不错的。

不错,有时在安全或者修改程序行为的名义下,我们会中断代码,不过,这种情况非常少见,而且一般来说,它揭示了在用户程序中的设计错误,或者是他们本来准备修复的那些错误,因为以前尚未意识到这里存在问题。如果是这样,当然很好,不过,如果是要以让代码更完美的名义来进行不必要的中断,我认为是一个错误。我在早年做过很多这样的事情,后来才知道这对于客户来说是行不通的。

至于说什么是好风格确实很难判断。

Anders:是啊。对我来说是好风格,而对你来说就未必是什么好风格。

如果回顾一下您涉猎过的语言,从Turbo Pascal, 到Delphi、J++、Cool,再到C#,那您的工作有没有什么主题呢?我可以听早期的莫扎特乐曲,然后再听他的安魂曲,我会说:“这些显然都是莫扎特的曲子"。

Anders:事事都是你所处时代的写照。我是伴随着面向对象之类的东西长大的。可以确定的是,从Turbo Pascal的中期到目前为止,我的工作核心一直是面向对象语言。那里发生的很多演变现在都已经发扬光大了。在Delphi中,我们在面向组件的模型上面做了大量的工作,比如说属性和事件等等。

有关这一点,我也把它用在了C#上面,这肯定是公认的。我试图一 直把握社区的最新动态,力争与时俱进。没错,Turbo Pascal是创新性的研发环境,Delphi是可视化编程,而RAD、C#和.NET则都与可控执行环境、类型安全等有关。你从身边的东西中学习,让它进入你的生态系统或竞争性生态系统。你确实想取其精华,去其糟粕。在这个行业里,我们都是站在巨人的肩膀。上的。它的魅力在于,相对于我们所见的硬件发展,编程语言的发展是何其缓慢。结果令人大吃一惊。

从Smalltalk-80开始, 我们已经经历了15或20代的硬件!

Anders:实际上,硬件是每大约18个月更新一代。然而,我们现在使用的编程语言和30年前的构想之间并没有巨大的差别。

译注1: 跳背游戏: 一个游戏者跪下或弯腰,队列中紧挨着的人从其身上跃过。

他们仍在喋喋不休地争论那些旧概念,比如说Java中的高阶函数。这种争论可能会持续10年。

Anders:这很不幸,因为我觉得他们应该在这个问题上进展得更快一些。我认为问题并不在于它是否有价值,而在于Java社区完成它是否有太多的流程和开销。

如果要在语言级别采用CPS(译注2)和揭露“Call/cc调用”,就会给你提供一个巨大的优势,你会怎么做呢,即便只有10%的程序员可能会理解它?

Anders:如果假设是的,那么这也是一 一个很大胆的假设。我认为并非如此,不过要看看我们使用LINQ(译注3)做了些什么。我确实相信,这将有益于绝大多数的C#程序员。能够跨不同的数据域,编写出更具声明式风格的查询,拥有一种合适的通用查询语言,这是它的最大价值所在。在某些方面,这就像Holy Grail语言和数据库集成。我们可能并没有解决整个问题,但是我认为我们取得了足够的进展,它证明了必须进行额外学习,同时,也有方法可供人们使用,而不必掌握来自第一原理(译注4)的lambda演算。

我认为这是函数式编程的一个很好的实际应用例子。你可以愉快地使用它,甚至不需要知道正在做函数式编程,或者有函数式编程原理在背后提供支持。我对这种结局非常满意。

您用了“实际”这个词。您是如何决定要添加哪些功能,拒绝哪些功能?在添加和拒绝之间,您采用什么标准呢?

Anders: 我不知道。随着时间的推移,你就会有这种判断能力:相当多的用户是否会从它所创建的概念化的过时观念中获益,对不对?相信我,我们的用户群给我们提供了大量的建议:“哦,如果我们只能做到这一点,”或者是“我确实喜欢做这些”,不过,有些时候,它太过于狭隘地关注解决一个具体的问题,而且作为一种抽象概念,它并没有带来很大的增值。毫无疑问,最好的语言通常都是由小团体或者个人设计出来的。

语言设计和库设计之间是否存在区别呢?

Anders:确实有很大的区别。API显然要比语言更具有领域特定的特点,而且如果你愿意这样说的话,语言确实要比API高一个抽象级别。语言制订了API设计框架,如果你愿意的话,甚至可以到夸克、原子和分子等细节。它们指示你把API整合在一起,而没有指示你API做什么。
在这个意义上,我认为二者区别很大。实际上,这又回到了我以前想要谈的内容。每当我们考虑为语言添加一个新特性时,我总是尽量让它适用于多个领域。优秀语言特性的标志就是,你可以以不止-种方式来使用它。这里,我再次举LINQ为例。如果你把使用LINQ做的工作分解一下,它实际上是大约六七种语言特性,包括扩展方法、lambda演算和类型推理等。然后,可以把它们放在一起,创造一种新的API。特别是,如果你愿意的话,可以把创建的这些查询引擎作为API来实现,但语言特性本身对其他方面也是非常有用的。人们使用扩展方法来处理其他有用的东西。局部变量类型推理等特性也非常好用。

译注2: 所谓延续(continuation),是指函数式编程中的一种函数调用机制。CPS (continuation passing style)是指延续传送风格。call/cc调用(call with current continuation)是指取得当前的延续,传递给要调用的这个函数,这个函数可以选择在适当的时候直接返回到当前延续。

译注3: LINQ,即语言集成查询,它是一组技术的名称,这些技术建立在将查询功能直接集成到C#语言(以及Visual Basic和可能的任何其他.NET语言)的基础上。借助于LINQ,查询现在已是高级语言构造,就如同类、方法、事件等等。对于编写查询的开发人员来说,LINQ最明显的“语言集成”部分是查询表达式。

译注4:第一原理(first principle),一个系统研究中的基本原理、规则或法则;该系统或体系的其他原理、规则或法则都是从它那里推导出来或从它那里得到解释,而它本身却不是从那个体系或系统中的任何其他原理或规则推导出来或得到解释的。数学公理和逻辑原理被认为具有第一原理的资格。

如果我们说:“让我们只是将SQL塞进那里或者完全是SQL服务器特定的东西,而且只是谈论数据库,然后想要使用LINQ这类的东西”,我们可能会更快一些安装LINQ之类的工具,但是它还没有那么通用,尚不足以成为通用编程语言的一部分。你很快就会成为-种领域特定的编程语言,而且你会同那个领域生死攸关。

你把自己很好的第三代编程语言变成第四代编程语言(译注5),这是一种通用的消亡之路。

Anders:是的。我对此非常清楚。现在, 我们正在考虑的一件大事就是并发。每个人都在考虑并发,因为他们不得不考虑这个问题。这不是想不想考虑的问题,而是必须考虑的问题。此外,在并发领域,我们能让语言指定一种特定的并发模型,但是,这是一 种错误的做法。我们必须从整体的角度来审视,找出语言缺乏什么能力,而这种能力会使得人们能够实现用于并发的大库和大的语言模型。我们需要这种语言要有一些处理方式,能为我们提供更好的状态隔离。我们需要功能的纯洁性。我们需要核心概念的不变性。如果你可以把那些作为核心概念添加进去,那么我们可以把它交给操作系统和框架设计者进行不同的并发模型实验,因为他们都需要这些东西。然后,我们不必猜测到底谁:是赢家。相反,我们可以相当轻松地知道结果: 一方会大为光火,由此证明另一方更为成功。我们仍然与此有关的。

这听起来好像是你想给人们提供工具来构建伟大的事情,而不是您指定他们要构建什么。

Anders: 我是想这么做。那样你就可以更好地利用社区创新。

您是在C#社区的什么地方看到的?大家会带给您代码吗?您去拜访客户吗?您会让您的MVP(译注6)在新闻组和用户组中闲逛吗?

Anders:它是以上几种方式的混合,再加上一 些别的方式。我们有Codeplex这样的代码共享XX。社区是多种多样的,还有商业性社区。既有开源的,而且还有很多的.NET开源代码。它们的来源五花八门。可以这么说,我认为并没有单点的汇聚。那边有一个多样化的复杂生态系统。

无论你走到哪里,你总会偶然发现一些东西,“哇,他们是想到这些的?”或者是,“这真让人吃惊”。你会意识到这会有多大的工作量。它可能在商业上并不可行,但是,它是-一件很好的工作。我的确在尽力密切关注与C#和LINQ相关的很多博客。

这些是我最喜欢的一些关键字,当我去博客闲逛时,只是去那儿看看发生了什么。它为你提供了很好的洞察力,看看人们是否已经熟悉了你的工作,无论你是不是通过正确的方式完成的。它教会了你面向未来的一些东西。

译注5:在计算机行业中,通常用几代来表示编程语言的发展。它是按照语言的抽象程度(即和自然语言的接近程度)划代的。第三代编程语言(3GL)是一种“高级”编程语言,例如PASCAL、C都属于这一类。而第四代编程语言(4GL)则包括VB、C++等。访问数据库的语言通常都是第四代。

译注6:MVP(最有价值专家)是指具备一种或多种技术专业知识,并且积极参与在线或离线的社区活动,经常与其
他专业人士分享知识和专业技能,受人尊敬、信任,而且平易近人的专家。

13.2 培育一种语言

Growing A lanuage

您是如何看待简单性的呢?

Anders: 存在真正的简单性,而且,还有我称之为simplexity的特性,这种特性我见过很多。当你第一次建立超级复杂的东西时,就会说,“哇,人们永远无法实现它。这实在是太复杂了,不得不为此竭尽全力。但我们必须要有所有这些功能。让我们来试试另外构建一一个简单的系统吧。让我们来试试能否仅在一个简单的界面里就把所有东西都包含进去”。

然后,你必须做的事情并不完全是系统被设计成什么样,梆梆!你陷入底层复杂性的大泥潭中,因为你所考虑的全部只是非常复杂的事物的浅层表面,而不是真正简单的东西。我不知道这是否会对你有启发,但我倾向于认为它就是这样的。简单性往往意味着你事半功倍。虽然只是花费很少的精力,但它的功效却能同其他做法一样或者是超过其他做法。这是事半功倍的精要。这不是用更多东西完成更多的事情,而是使用一个简单的表层。

如果您现在要创建一种新的编程语言,您会遵循这个原则吗?

Anders:噢,肯定会的。到目前为止, 我已经创建了多种编程语言,当然还肯定会有许多实现。在着手创建新语言之前,你必须非常非常清楚你为什么要这么做以及你要解决什么问题,我认为这是非常重要的。

通常,在新编程语言方面,人们爱犯的错误是他们执迷于某个特定的问题。或许这种编程语言可以正确地解决那个问题,因此他们着手解决这个问题的一部分,也许他们会干得非常出色。那么,每一种编程语言,我的意思是每一种编程语言,都是由10%的新东西和90%的编程支撑要素组成的,而这些支撑要素又是必需的。在这些新编程语言中,我们所看到的这些创新的解决方案都是这10%的东西,但另一方面,为了让你真正能编写程序,每种语言都必须有的那90%又非常糟糕,因此,他们失败了。

每一种编程语言都必须有许多枯燥的标准要素,理解这一 点是非常、非常重要的。如果你没有正确地理解它,你就会惨遭失败。相反这意味着,如果不是创建一 种新的编程语言,你可以在现有的编程语言上进行发展演进,然后这种“综合的产品”看起来很不同,因为你已经包含进去了90%的内容。事实上,你已经包含进去岂了100%的内容。你只不过想往里面添加新东西而已。

就像C++一样?

Anders:就像C++一样,它就是C或者是我们已经完成的C#不同版本等演变而来的一 一个很好的例子。我是非常相信发展演变的人。当然,有时候,你就不能往那里添加更多的东西,在你添加的新东西和语言中无法移除的旧方式之间需要大量的平衡。创建一 种新语言确实是规则的例外多过规则自身。

您会创建一种通用语言或领域特定的语言吗?

Anders: 我认为,真正的答案是“都不会”。我非常想通过创建一种通用编程语言来解决问题,而这种语言擅长于创建领域特定的语言。再者,使用这种领域特定的语言,我们面临的难题是:它们在领域内表现很出色,但将它们作为通用语言则非常勉强。各种领域特定的语言确实都需要一些通用的特性。除非这种领域特定语言只是一种纯粹用于描述数据的数据定义语言,那样的话,依我看还不如干脆使用XML。

如果你是拥有逻辑、谓词、规则等的真正的编程语言,你就必须有各种表达式,表达式还必须有运算符,或许你还必须有标准函数,而且你的客户想要完成你从来没想过的事情。还有你需要的一 大堆标准要素。如果你能在通用编程语言的基础上创建领域特定语言,那么我想这比你每次都从头开始要好得多。

现在,通用编程语言面临的一个难题是它们在创建内部DSL方面变得越来越好,你可以看看LINQ这个例子,但是它们目前并不擅长获取这些内部DSL的正确使用模式。在某些方面,当你创建内部DSL时,实际上你想要限制可以使用通用编程语言来做的事情。您希望能去除语言的通用性,而且你只想让DSL具备这种功能。现在,通用编程语言在这方面并不擅长。这也许是值得考虑的事情。

Brian Kernighan说,如果你想创建一种通用语言,你就应该从铭记这一目标开始。否则,如果您创建一个小的语言,一旦人们开始使用它,他们就会要求添加新特性。培育一个DSL效果通常不会很好。

Anders:噢,是的。我想起来Gosling说过,每一个配置文件最终都会成为它自己的编程语言。这话说的很对,你应该认真对待, 你说过,在某些方面平台比语言更重要。

那我们能生成可重用组件吗?

Anders: 哦,我说这话的原因是,如果你回顾一下语言、工具和框架在过去的25年、30年中的发展,那你就会对编程语言只有很小的变化感到诧异。同样让人感到诧异的是,我们的框架和运行次数是如此之大。较之25年、30年前,它们可能是增大了3个数量级。我最初做Turbo Pascal时,运行时库或许只有100到150个标准函数,当时情况就是这样。而现在,.NET框架有10000个类型和100000个成员。显然, 利用好这些资源变得日益重要。因为它重塑了我们思考问题的方式,而框架变得日益重要,则是因为正是它改变了我们的编程方式。

现在,“利用”就是一切。从编程的观点来看,你的计算机基本上就是一个无底洞。你从现在开始写代码,就是一直写到死,你都不能填满它。它的容量很大,而最终用户的期望会变得越来越高。你真正能获得成功的唯一一条路就是寻找一些利用现有资源的灵巧方法。如果你回到25年或30年前就不是这种情况了。你只有64k内存可用,好的,加油,你只需要一两个,月时间就能把它填满。

语言会对程序员的生产率造成多大的影响呢?程序员的能力又能造成多大的区别呢?

Anders: 我认为这两种因素是同时起作用的。我认为语言会影响我们的思考方式。如果你愿意的话,程序员的工作就是去实现这一想法。那就是原始素材、原始动力在推动这个过程。是语言在重塑你的思考:它的作用实际上是帮助你以一种高效的方式思考。例如,支持面向对象的语言如何促使你以某种特定的方式来思考问题。函数式语言促使你以另一种方式来思考问题。动态语言可能促使你以第三种方式思考问题。不同的语言可以促使你进行不同的思考。有时尝试从不同;角度来处理问题是很有益的。

你会更喜欢添加一种语言特性让每个人都提高一 点效率,还是喜欢让少数开发者变得更加高效呢?

Anders: 对于一种通用编程语言来说,只为帮]助少数人而添加特性并不是一个好主意,因为你最后会成为一个装满奇怪东西的杂货包。所有优秀语言特性的标志是,它有很多优秀的应用,而不只是一个。如果你回顾一下我们在C#3.0中添加的所有东西,它们整体构成了所谓的语言集成查询(即LINQ)的概念,实际上,它可以分解为六七个互不关联的语言特性,而且每一一个都有很多优秀的应用。它们并不仅仅有益于某个特定的程序员。它们处于一个更加抽象的层次上。对于每一项优秀的语言特性,你必须能够展示它能让你在某些场景下受益,否则它就不适用于该语言。或许让它成为一个API特性会更好一些。

你认为添加或者删除哪些特性会使调试更容易?在语言的设计过程中你考虑过调试的体验吗?

Anders:噢,绝对是这样的。如果你看一看整个C#的基础,这种语言是一种类型安全的语言,它意味着没有数组溢出或者野指针(译注7)的现象。一切都有定义好的行为。在C#中就没有未定义行为之类的事情。错误处理是通过抛出异常来完成的,而不是返回你可以忽略的代码。因此,像类型安全、内存安全和异常处理这样的每一种支撑都非常有助于消除整个bug类,或者是更易于发现整个bug类。那是我们一直都在思考的事情。

您是如何在不限制开发者的前提下预防重复性问题的?在开发者的安全性和自由度之间,您是如何选择的呢?

Anders:我认为,如果你愿意的话,每一种语言都会在某一范围内具有影响力和生产力。C#肯定是一种比C++更安全和更受保护的语言环境,比起你正在编写的汇编代码,C#将会更加安全和更加高效。纵观整个编程语言史,编程语言的普遍趋势是:保持抽象层的较高水平以及促使编程环境更加安全,或者是把程序员必须要做的越来越多的事务性工作交给机器来处理,让程序员主要关注过程的创造性部分,这样才是真正地提升他们的价值。程序员们非常讨厌把内存管理作为一种规则。他们同样非常讨厌类型安全分析; 因此,我们就有了bug。

在某种程度上,我们可以把它交给机器来完成,而让程序员放手进行创造性的思考,我认为,这是一种很好的折中平衡。它的代价只是一小点的性能损失,不过小伙子,它并没有损失太多。今天,在一个典型的.NET应用程序中,如果你观察一下程序的执行概况,并看看程序的时间花在了什么地方,甚至很少会看到垃圾收集。然而,你的程序是安全的,而且也没有内存泄漏。这是一种很妙的折中平衡。比起在C++或者C中这些人工内存管理系统来说,这一点真是棒极了。

我们能够使用一种科学的方式来设计和改进语言吗?我能够看到实现的研究结果给出的改进,不过语言设计听起来好像与设计者的个人喜好有关。

Anders:我认为编程语言设计是艺术和科学的有趣组合。很显然,这里面有大量的科学,比如用于分析、语义、类型系统以及代码生成等的符号数学形式体系。这里面融合了大量的科学和工程知识。

而且,它还是一项艺术。这种语言感觉像什么呢?当你用这种或者那种语言编程,脑子里想些什么呢?它们又有怎样的不同呢?对于人们来说,什么更容易理解,什么很难理解呢?我认为我们并不能度量这些东西。

它从来不是很科学的。它总是从纯艺术的角度来看待语言的设计。就像绘画也有好坏之分一样,你只能从某种程度的科学上说:“当然,这种组合不一定合适。或许他没有用对颜料”。但这最终是仁者见仁,智者见智。这只是一些你无法形式化的东西而已。

译注7: 野指针(stray pointer), 也就是指向不可用内存区域的指针。通常对这种指针进行操作的话,将会使程序发生不可预知的错误。一般有两个原因: 1.指针变量没有被初始化。2.指针被释放或者删除之后,没有置为NULL,让人误以为它是合法的指针,

您至少会说两种语言,您认为这在某些方面会有帮助吗?有时意大利语只用一个单词就可表达某个概念,而英语却得用一句话,显然反之亦然。

Anders:我不知道。这个问题问得很好。我从未想过这个问题。或许是吧。我当然会认为成为一名优秀的语言设计者必须要懂得多种编程语言,这一点是毫无疑问的。至于说它是否有助于理解多种口语,我真的不知道。这两者应该联系得很紧密。在设计团队中,我们可以肯定有人可以说很多语言,并擅长于音乐。他们好像存在着某种联系,不过,我并不确定它们是如何联系的。

13.3 C#

C#未来还能存在多长时间呢?你已经说过是10年左右。

Anders:C#项目始于1998年的12月份,因此,我们正在讨论10周年纪念。这并不是说已经在行业中存在了10年,而是从我们内部开始做的时候算起来是10年。我认为我们至少还会使用10年,不过这要视情况而定。我说过,我已经不再对这个行业遥远的未来进行预测,因为从来没有人能预测准确。不过,我的确看到了C#繁荣而又健康的未来。我们并没有创新,但我们仍有大量的工作可做。

从应用领域立场来回顾C#的发展,我看到有这样的需求:C#很有希望取代C++语言成为一种系统编程语言。

Anders:它的确可以应用在那方面,不过,在更适合使用.NET或Java等语言的可控执行环境中,C#也有很多应用。

我拿C#与Java做了一下对比,结果发现,C#的发展动力似乎更为强大。Java那些人看起来好像想要一个基准,在这条基准上每个人的代码都差不多是相同的。无论是你已经编了10年Java程序,还是在之前从未编过程,或者只是刚上了六个月的Java培训班,你们的代码都统统差不多。C#看起来从Haskell或者F#中获得了一些新观点。是否要添加这样一种特性,即使是上完6个月C#课程的人们也无法马上理解?

Anders:我这么说并不是表明是要设计下一个COBOL; 我们只是借用这种表达方式而已。

我们看到因特网革命和电子技术革命的动力何在呢?动力在于我们在不断发展的这个事实。让我来回顾一下这种进步。一旦你停止了发展,我不知道你还能增添什么价值。再者,这是从极端的观点来看的。当然,平台的稳定性很有价值,不过,我认为你是通过确保向下兼容来提供的这种价值的。在C#1.0中,你可以随便下车但不能走开。对于那些真正想要更高效率或者想要构建更新的(比如说SOA一类的)应用程序的人们,或者更具动态编程风格(比如可改编程序)或者更具声明式编程风格(比如说我们正在使用的LINQ)的人们来说,要么你就改进这种语言,要么你就放弃这种方式,否则就会被别的东西取代。

你收到过关于C#语言的反馈吗,而不仅仅是实现?

Anders:我们每天会获得关于这种语言的不同方式的反馈。可能是人们给我发邮件。我阅读人们的博客,浏览人们讨论技术性问题的论坛,我还会参加会议,这样,每天会通过各种各样的方式获得有关该语言优劣的反馈。我们会把这些反馈给设计团队,而且我们还维护关于这些疯狂想法的长长的详细清单。其中有一部分从未添加到语言中,不过还是把它们维护在列表中,因为或许有一天会从其中获得一些好主意。我们知道虽然做得并不到位,但还是愿意做事的。

随后,我们逐渐地找到了问题的解决方案。其中一 些只是人们讨论的简单问题,那恰好也是我们要解决的。另一些确实是人们从未谈论过的大问题,比如说LINQ等。这不像有人曾经问过我们的:“我们喜欢把查询嵌入到语言里”,因为你并没有真正考虑这些你能考虑的概念。

我不愿意说我们从某种特定方式获得反馈。这是一一个有机的过程,我们可以从很多不同的地方得到它们。毫无疑问,如果没有这些反馈,我们就无法设计出这种语言,因此,这一切都是以倾听产品使用者的意见为基础的。

您是如何管理设计团队的?你如何做出决策?

Anders:首先,你从用户获得反馈时,通常用户会告诉你,“如果你能够添加这个特定特性,我们会非常喜欢”。随着你的深入了解,结果是,哦,他们试图又想做这个还想做那个,而且典型地,人们还会告诉你他们的解决方案。现在要做的是发现他们的实际问题,然后设法把它们融入到更大的语言框架中。从某种意义上来说,获得反馈的第一阶段是去侦探问题所在,并理解隐藏在客户要求的解决方案下的真正诉求。他们的真正问题是什么?

然后,我才考虑决定以何种方式来解决它。在改进一 种语言时,你必须始终认真对待强加到语言中的一大堆功能,因为你添加的功能越多,你的语言就老化得越快。最终,语言会因不堪自己的重负而被淘汰。那里东西太多了,而且很多东西是相互冲突的。
你必须非常非常明智地对待添加的东西,因为实际上,你并不想只是出于历史原因而使用三种方法来完成同一个功能。
因此,有很多次我们会说,“是的,如果我们能够重新开始,我们肯定会立刻加上人们要求的这些特性”。既然我们无法重新开始,我们就不能这么做,因为这是非常基础的东西,我们不能通过接连出击而从根本上改变它的属性。我们唯一能做的是使它变为双头兽,而这并不是我们想要的结果。

就设计过程自身而言,我们拥有一个非常强大的C#设计团队,它由6~8名定期开会的成员组成。在过去的10年里,每周一、周三和周五下午的1:00到3:00都要定期开会。其中有一些会议取消了,不过在10年里,我们都在日程表里给它留出了时间,而且这种情况还会继续下去。在这个过程中间,人员也有变动。我自始至终参与了这项工作。Scott Wiltamuth也差不多是这样。其他人进进出出,变化不定,不过这个过程已经存在了很长时间。

我们把这个当作我们的设计职责。这就是我们一直在进行设计的原因。为了保持产品的连续性,把设计当成一个持续的过程是非常重要的。通常人们会突然拼命努力去干,“噢,该做下一版了。我们开个会讨论一下该怎么干吧”。然后,就没完没了地开会,然后人们纷纷离开,最后在这一年里没有做任何的设计。一年过去了,又到了开发下一版的时间了,你连最初的那拨,人都凑不齐了。最终只能发布一款令人抓狂的产品,而每一版发布都会让人感到面貌各异。如果你继续进行设计,你发布的产品几乎就像是一个个性化的产品。

灵感乍现可没有什么时间表可言。它们只会偶然迸发。如果你并没有一个获取灵感的过程,如果你没有马上进行设计,那么或许这种灵感就会消失。我们总是不断地对将要开发的下一版进行持续的探讨。我认为这么做的效果确实很好。

C#有一个ECMA标准化流程(译注8),这在语言中是很罕见的。这样做的动机是什么呢?

Anders:对于很多人来说,标准化是采用技术的一种要求。毫无疑问,在很多地方(不仅仅是企业),如果你看一看政府部门,标准化确实是一种需求。学术界也是如此。实际上,对于微软的标准化来说,它有很多令人瞩目的好处。无论何时,只要是我们构建像.NET这样的技术,它总是能够在第三方提供技术的其他平台上实现,而且,你可以选择让它们随意地复制你已经创建的东西,并发现故障,然后获得一 些糟糕的经验。这对那些客户也意味着是糟糕的经验,这些客户基本上都依赖于你的那些东西,不过要在他们所有的遗留硬件上进行另外的实现。

当你把这些都汇总起来,实际上即使是从商业的观点来看,做这些也都是有意义的。你所构建的东西本质上有很多优点,它也是作为在这些方面非常精确的优秀强制性函数而存在。事实上,我们标准化C#就意味着我们必须编写一个非常简明、非常精确的语言规范,仅仅是从内部的立场来看,这项工作的投资已经给我们带来了许多倍的回报。

就来自质量保证部门的更好的测试框架而言,以及就针对于实现新语言特性的更好的研究手段而言,对于原型编译器,它们所期望或要求的东西是非常清晰的。针对该语言的可教性,非常简明的规范意味着人们总能把它当作参考资料来查阅,否则就只能猜想了。它有助于确保代码的反向兼容性。因此不管怎么说,可能会有很多你无法立即想到的好处,不过实际上它们确实存在。通过标准化过程,你可以让见多识广的社区来关注你的产品。我们已经获得了参与标准化过程的其他公司和个人的大量反馈,而且,这样也使得C#变得更好。那也是很有价值的。我不能确认如果不是因为我们的标准化工作,这些组织和个人是否会对它感兴趣。

然而,标准化会滞后于语言的发展。

Anders:是的。在某种程度上,标准化会减慢你的速度。这还得看你怎么说了。有些标准可以说成,“你必须把这个实现了,而不用实现别的,而且如果要扩展我们指定的东西,那就违反了标准”。我从来不相信那些。标准的目的或者是被要求建立一个通用基准,而且毫无争议的是,还要有一种方式确保你在遵从这条基准而没有逾越它。但是,标准肯定应该为创新留出自由空间,因为你要通过那些创新来制订V2版本的标准。你不能宣告它是非法的。

对于C#,它有一个标准,不过这个标准并没有使我们远离发展。一个创新的过程不会发生在标准过程之外,因为你不会跳出标准社区而获得认可。那不是它的目的。不管你工作在什么框架上都必须允许创新随时出现。

在语言设计的形式方面您持何种观点呢?一些人建议您应该首先制订书面的形式规范,然后再编写代码。一些人恰恰完全忽略了形式规范。

Anders: 答案是尽量不走极端。我认为,如果根本没有形式规范,那么该语言通常就会乱成一团。如果首先使用形式化方法指定一切,然后再事后实现编译器,这样的语言也会很难使用。我们开发C#的方式是同步编写编译器和语言规范,而且二者还相互深人地影响渗透。我们在编写编译器时可能会偶然遇到一些问题,这时候必须回过头来在规范中解决。或者,在编写规范时尝试严格分析所有的可能,“吁!!或许我们应该在编译器中试试别的办法,因为有很多我们没想到的其他情况”。

译注8: ECMA是“European Computer Manufactures Association”的缩写,中文称欧洲计算机制造联合会。它是1961年成立的旨在建立统一的电脑操作格式标准—— 包括程序语言和输入输出的组织。ECMA标准是C#语言所有功能的官方说明。

我认为这两点都很重要。我们做了标准化工作,我对此非常高兴,因为它促使我们更清楚地把握了语言是什么和它如何工作这两个问题。它还促使我们拥有了一个形式化规范说明,而这一一点,正像你所说的,恰恰是一些语言没有的东西,那并不是一件好事。如果源代码就是“规范说明”的话,那就意味着,一旦你想要搞清楚这个特定的程序将会发生什么,你就必须去看一看要编译的源代码。并没有多少人能做到这一点。唯一的替代方案就是靠猜测,或者是编写测试用例来看:看结果,并寄希望于找到所有的边界条件。我认为这不是正确的方式。

顺便问一句,您如何调试C#代码呢?

Anders:我主要使用Console.Writeline这个调试工具。 说句老实话,我认为很多程序员都是这么干的。对于更复杂的情况,我会使用一个调试器,因为我要看一看堆栈踪迹或者是本地出了什么问题等。不过通常情况下,你只要简单地检查一下,就能够很快地摸清底细。

设计API时,您遵循了什么原则吗?

Anders:噢,首先我会说要保持它们的简单性,不过这将意味着什么呢?我的意思是说这听起来很愚蠢,对吗?我极为推崇方法和类都尽可能少的那些API。有些人相信越多就会越好。我不是这么想的。我想,看看你认为人们会怎么样来使用你的API是非常重要的。找到五种典型的使用情景,然后确认这些API会使事情变得尽可能地简单。理想的情况是它只不过是一个API调用。你不应该为了典型的情景而必须编写很多行与API无关的代码。在这一点上,它并没有处在一种恰当的抽象层次上。

不过,我还认为在API中忽略一些东西是重要的。你想要从简单地使用API开始,然后无缝地移动到你需要的更高级的应用中。许多API有这样的渐进功能。没错,你只能调用一些简单的方法,不过,当你想完成稍微高级一点的功能时,突然,你却一败涂地。现在,你必须为未来高级一点的功能去学习一些与你无关紧要的东西。我非常推崇这种循序渐进的方式。

文档怎么样呢?

Anders:总的来说,软件的文档状态是很糟糕的。我一直力劝程序员并且也在内部一直呼吁,但应者寥寥,不过我告诉程序员,对于你交付给用户的API来说, 一半的功劳要归于优秀的文档。如果没有文档来解释API是什么、应该如何使用它,那么再出色的API也毫无价值可言。这是一个难题。许多公司喜欢让程序员编写代码,而让其他人来编写文档,而且他们两者之间没有什么沟通。最后,你的文档上面只是写着“MoveWidget,移动窗口小部件(widget)”,或者对一件显而易见的事情却哆里哕唆地说上半天。这真是让人啼笑皆非。我认为,比起他们编写的代码来说,程序员应该编写更多的文档。

您喜欢在代码内添加注释,或者你考虑过使用外部文档吗?

Anders:我一直倡导在代码中添加XML文档注释。如果你把它放入代码,使用它的程序员就有机会注意到文档注释说得不对的地方。或许他会把它修改好。如果你把它放在别的文件里,那么程序员绝不会再看它, 因此它永远也不会改正。

大家都试图使它们尽可能地接近。尽管没有绝对的完美,但是我们在一直朝着这个目标努力。

对于成为一名优秀的C#程序员,您有什么建议?
Anders: 这是很困难的。在C#编程方面有很多好书,而我只是鼓励人们从中选出一本来看。我在这里不会点名,不过,有很多好书会帮你成为一名优秀的C#程序员,并且帮助你更好地理解.NET框架。在网络上也会获得很多有用的东西,比如说Codeplex等。你能获得大量的开放源代码项目供研究和学习。

一般来说,对我成为一名优秀的程序员有帮助的是了解不同的编程风格和不同类型的编程语言。在过去5到10年里,我通过研究函数式编程学到了很多东西,这是一种截然不同的编程方式,不过它能教会你很多知识。显然,对于编程来说,这是一种不同的角度,而且它会让你以一种不同的观点来看待问题,我发现这是非常、非常有用的。

13.4 计算机科学的未来

The Fulure of Computer Science

您认为计算机科学的突出问题是什么呢?

Anders: 如果你是在一个元层次上来看待这个问题,语言发展中一如既往的趋势总是抽象层次的不断提升。

如果你回顾一下从外部指令(plugboard)、机器代码、符号汇编程序、C、C++一直到现在的可控执行环境等的发展,我们迈出的每一步都关乎抽象级别的提升。最重要的挑战总是去寻找下一个抽象级别。

现在,最大的挑战好像是有若干个竞争者。一个是我们已经说过的并发性:创建可用于并行的有意义的编程模型,并能够被各个层次的编程者所理解,而不是只为少数权威(high priest)而提供的平行性。我们现在所处的世界正是这样。现在,甚至权威们也会为他们自己的代码感到吃惊。这是我们面临的-一个很大的挑战。

最近在社区里也有大量关于领域特定语言和元编程的讨论。在我看来是说得多干得少。我认为我们并不知道答案。你把事情看成是面向方面编程和意图编程(intentional programming),不过我们必须真正彻底了解它。人们可能会说“噢,没有领域特定的语言”或者是“噢,领域特定的语言无处不在”,这要取决于你问谁。在某种意义上来说,我们甚至无法就特定领域的语言是什么达成共识,但是很显然,它为更清晰无误地表达你自己提供了更多的方式。在某些方面,我们已经用尽了命令式编程风格所提供的所有方式。我们并不想用我们的命令式编程语言实现更多功能。它不可能像我们添加新语句那样,使我们的效率突然提高10倍。

我认为当今的大多数编程语言都是这样的,它们会强制你过于详细地描述问题的解决方案。你得编写嵌套的for循环和if语句等等,而你只不过是想将两段数据连接起来。不过它不允许你这么干。你不得不委曲求全,开始做哈希表和字典,等等。

问题是我们如何转移到更具说明性风格的编程上来。当然,你的层次提升得越高,你最终拥有的概念就越多,因为你的领域特定性更强。对于领域特定的语言的这种梦想早已是老生常谈,然而我们还没有找到合适的方式来实现它们。我的感觉就是如此。因此,这仍然是一个挑战。
现在,我们看到了动态编程语言的复苏。实际上,我感觉确实是这样,尽管是语言的动态性越来越强,因为它们拥有强大的元编程能力。正如你看到的Rubyon Rails那样,Ruby的元编程能力让它的功能变得更加强大,而不仅是只有动态性。恰好,动态语言中的eval函数和元编程要比在静态语言中容易得多。另一方面,放弃你的陈述式完成(statement completion)和你的编译时错误检查等须要付出很大的代价。

我已经看见过很多人在争论动态语言时说的是Smalltalk的浏览器。

Anders: 我不确认我会买这个账。当你的问题足够小时,它是对的,在Smalltalk刚出现时它一向只是一个足够小的问题。由于现在框架的规模的增大,即使人们想去了解,也难以想象他们会真正了解特定对象中的所有的API。由编译时元数据和静态类型所驱动的陈述式完成、智能提示(IntelliSense)或重构等工具是颇有价值的。它们的价值会越来越大,因为世界将要变得更加复杂。现在,我们看到大量的动态编程语言层出不穷,不过我认为它的兴盛大多是因为 (1)是以元编程的角度看待的; (2)在很多方面,这只不过是对于复杂的J2EE环境的一种反应。

我的确看到很多Java程序员转而使用Ruby,而仅仅是因为他们深受框架Struts, Spring和Hibernate等的困惑。除非你是一个技术奇才,否则你将不可能凭借一己之力将这些东西融合到一起。

对于那些不是也不想成为“魔法大师”的人来说,编程会更容易吗?

Anders:我想是这样的。这完全取决于你通过编程所要表达的意思。因为在某种意义上来说,你是在使用一个电子表格编程吗?如果你可以让人们在编程时甚至没有意识到他们正在编程,那么,哦,我的上帝,那就太棒了。对于教会全球的用户能像程序员那样在我们今天使用的编程环境中编写程序,我对此不抱任何希望。毫无疑问,是编程,没错,不过应该在一个更高的级别当中。

我们现在和在未来五年里将会面临什么问题呢?

Anders: 并发是目前很大的一个问题。问题已经是迫在眉睫,而我们也已经开始去寻找解决方案。在可预见的将来,我的最大挑战之一就是让我们的团队着手解决这个问题。

再者,我们愿意以一种发展的方式做这件事情,但是,你如何在没有打破现有代码限制的情况下来处理共享状态问题和副作用呢?我们也不知道答案,不过并发性是所有新语言和新框架都要具有的一个足够大的模式变化。尽管我认为我们还没有达到这个层次。

我认为,让人们有可能编写本质上并行的API,而且是由真正理解某个特定领域的人来编写, 无论它是进行转换、数值处理、信号处理、位图或者图像操作,我们在这些方面都可以大有作为。不过,把API加入到它上面,从外部看来很像是同步方式,在某种意义。上来说,它与内部的API的并行性就隔离开来了。

为了使我们能够正确地做到这一点,目前的编程语言中还需要一些东西。我们已经做到的就是将代码作为参数来传递。由于API的功能变得越来越复杂,你不能只是给API传递简单的值或者数据结构。你应该能够将代码段传递给API,然后进行编排和实现。

你需要高级函数和抽象,比如map、fold和reduce.

Anders: 高级函数。确实如此。为了能够做到这一点, 你需要用到一些东西,比如说lambdas和闭包等。对你来说,为了能够在并行环境中做到这一点,你还需要保证这些lambdas是否纯粹,或者它们是否具有副作用。我能自动地并行运行它吗?或者,它们会产生副作用吗?我怎么能知道这些呢?现在,我们的语言当中还没有这些东西,不过,我们肯定会考虑把它们加进来。当然,添加它们的诀窍在于,它对你不会有太多限制,而且也不会过多地破坏你的现有代码。这是一个巨大的挑战。

我们团队每天都在思考这些问题。

有必要为并发性而改变语言实现或语言的设计吗?

Anders:噢,它确实改变了语言的设计。很多人抱有这样的希望:一个人只能够拥有一个编译器的并行交换,而且你只是想说,“并行地编译它”,然后它会运行得更快,并且是自动并行化。从来不会发生这种情况。人们已经尝试过,这对于我们在主流语言中(比如C++、C#和Java等)使用的命令式编程风格是不会起作用的。这些语言很难自动并行化,因为人们在程序中过于依赖它的副作用。

你要做一些事情。首先,要为并行性构建现代API,它们的层次要比线程、锁和监视器以及我们现处的层次更高。

接着,你要从语言中得到一些东西,从而使编程更容易而且更安全,比如保证对象的不变性、你所知道的没有副作用的纯函数、分析孤立的对象图,这样你会知道一幅对象图的特定引用是否已经被其他人使用过。如果它没有被使用过,那么你可以安全地mutate它,如果它已经被使用过,就可能会有副作用。诸如此类;在这种情况下,编译器可以做出一些分析并有助于保证安全性,就像我们今天拥有的类型安全和内存安全等一样,为了能够更好在这些并行系统上编程,我认为在未来的5到10年里将需要这些情况。

本质上你是在告诉计算机去做什么。

Anders:这其中的一个大问题是,我们现在使用的命令式编程风格实际上是描述过于详细(overspecified)。而且这就是为什么难以实现自动化并行的原因。

未来,我们有可能让框架来完成处理并发的工作吗?

Anders:噢,我想可能。并发有很多不同类型,不过如果你是说数据并行方式的并发,它要在大型数据集上进行运算(比如图像处理、语音识别或数据处理之类的运算)时,我认为拥有一种可视为API的模型是非常可能或是非常适合我们的。如果你能对API说:“这是我想要使用的数据和操作。你去完成它,并在给定可用CPU数量的情况下尽可能快地做这些事情”时,你已经拥有了高阶API。

这非常有趣,因为现在你可以相当轻松地说“数据在这儿”。对于一些大数组或者一些对象等,你可以只给出引用。指定运算通常须要给出代码段的引用,如果你使用委托(delegate)或者lambda演算,而且如果编译器能够分析并保证这些lambda演算没有副作用, 而且如果有副作用就会警告你,这无疑是很好的。那是我所说到的一部分,不过,那只是并发的一种类型。在异步性更强的分布式系统中,还有其他类型的并发,在编程语言中支持这种不同的并发,我们也会从中获益。如果你看看Erlang这样的语言,它应用于高度可扩展的分布式系统中。它们有一个非常非常不同的编程模型,它更具函数式特征,而且是基于异步智能代理和消息传递等机制。我认为我们的语言也可以把那些令人感兴趣的东西吸取进来。

面向对象范式会带来什么问题吗?

Anders: 这取决于你如何对面向对象范式归类。多态性、封装和继承本身不再是什么问题,尽管函数式语言在如何使用它们的代数数据类型来处理多态性方面,典型地具有一种不同的观点。除此之外,我认为,面向对象编程的最大问题是:人们要以一种极具“命令式”风格的方式来进行面向对象编程,其中,对象将可变状态封装起来,而且你调用某些对象的方法或者向那些对象发送消息:引起它们修改自己,而引用这些对象的其他人则不知道。现在,你最终只能对你无法分析这个副作用感到惊讶。

从那个意义上来说,面向对象编程是一个问题,不过你可以使用不可变对象(亦称常量对象)进行面向对象编程。然后,你就不会再遇到像那种函数式编程语言才有的问题。

就您对函数式编程的兴趣而言,学计算机科学的学生应该为了函数式编程而学习更多的数学和做更多的实验吗?

Anders: 噢,毫无疑问,我认为在任何计算机科学课程中加入函数式编程内容是非常重要的。你是否要从它开始,那要视情况而定。我无法确定你最开始的编程导论是否应该是函数式编程,不过我毫不置疑地认为它应该是课程的一部分。

*人们应该从你的经历中学到什么经验和教训呢?

Anders: 噢,如果你看一看我最初的作品Turbo Pascal,它就反映出我不喜欢采用传统方式来做事。别害怕。
那只是因为人们说你无法做到,并不是你真的无法做到。只是他们无法做到而已。我认为,利用创造性思维来考虑问题,并设法为现有的问题找到新的解决方案,这总是非常令人开心的。

我认为简单性一直是一个赢家。如果你能找到-一个更简单的解决方案一毫无疑问,这是我一贯的指导原则。我一直设法使它更简单。

另外,我还认为要想真正在某些方面有些特长,你必须对其充满热情。那是你学不来的东西。而你已经有了那种东西。我对编程感兴趣不是因为我想赚很多钱,或者是因为有人告诉我应该这样做。我对它感兴趣完全是因为它深深地吸引了我。你无法让我止步。我必须编写程序。它是我想要做的唯一一件事。我对它非常非常地狂热。如果你想要真正地擅长某些事情,就必须对此充满热情,因为它需要你投入大量的时间,而大量的时间的投入正是真正的关键所在。你需要完成很多的工作。



版权声明

当前位置:极客学长 | 极客中心 » 编程之魂之C# – 与C#之父Anders的访谈

发表评论

Captcha Code

我还会在以下平台发布内容

知乎 CSDN