煮酒论坛

 找回密码
 申请新用户
搜索
热搜: 活动 交友 discuz
查看: 6095|回复: 0

微软架构师谈编程语言发展

[复制链接]
发表于 2009-7-8 11:42:50 | 显示全部楼层 |阅读模式
转自:http://blog.csdn.net/hellothere/archive/2007/07/29/1715993.aspx
本文是对微软Channel 9中采访几个语言大牛的视频的翻译。
视频在Channel 9,链接http://channel9.msdn.com/Showpost.aspx?postid=273697
名字为Anders Hejlsberg, Herb Sutter, Erik Meijer, Brian Beckman: Software Composability and the Future of Languages
大家可以找来看看。
个人感觉这些大牛高屋建瓴,有点有面地谈到了多个语言的发展和语言的相互关系,对于我们开拓视野非常有帮助。由于只能靠听来翻译,篇幅又长,只能分段(估计有4-5段)慢慢来。而且,水平所限,难免错误,请大家指正。

微软架构师谈编程语言发展(一)
译者:程化

Charles:好的。今天我们请到了微软设计编程语言的大师们。请你们介绍一下自己。
(译者注:Channel 9的主持人,从其对话来看,应该是编程出身,对于程序有很好的理解)

Herb:我是Herb Sutter,我是VC++小组的架构师。
(译者注:C++标准委员会主席,Exceptional C++系列的作者,C++领域的大牛人)

Erik:Erik Meijer,我在VB以及C#小组工作。
(译者注:先是SQL Server组的架构师,现为VB、C#组的架构师,从事把CLR、关系数据库、XML数据合为一体的伟大事业)

Brian:我是Brian Beckman,和Erik Meijer一起工作。呵呵
(译者注:物理学家,天体物理为主,业余时间写程序,包括编译器,自称来自从事影视娱乐业的家族,家里以其从事科学研究为奇)

Anders:我是Anders Hejlsberg,我的技术领域是C#。
(译者注:微软的“技术小子”,公认的牛人,C#的主要设计者,.NET框架的重要参与者。微软之前,Anders是Borland的工程师,Turbo PASCAL的主要开发人员,Delphi的首席架构师)

Charles:我们今天访谈主要讨论两个相关的论题:可组合性(Composability)与编程语言。作为程序员,当我们构造系统时,总是要面对这两个问题。你们是创设语法,搭建架构的人。所以,我想讨论的一点是,你们是如何协调工作的?三个语言——C#、VB和C++,都在演进,同时又服务于不同的目的,C++更多服务于系统级,C#和VB更多偏向应用层面。而且,语言在不断创新(译者注:谢谢ponda的修正)。这一切是如何形成的?你们一起工作吗?你们是如何决定语言的创新的(译者注:谢谢ponda的修正)?你们是一起设计,还是想到什么后再与他人共享?很抱歉提这样的怪问题,请试着回答。

Anders:我想,你说的两种情况都存在吧。事实上,早在我们做LINQ之前,Erik就在Comega项目做了很多工作了。在LINQ和Omega之间有很多相似之处,有很多互相影响的部分。我们一直在讨论相关的问题。而且,Erik实际也在C#设计组中,所以,我们总是就当前的工作及时交换意见。VB组和C++组的人也在一幢楼里工作,大家经常碰到一起。所以,我认为这一切是相互渗透,以及不断聊天的结果。

Charles:但是我的意思是,你们是否也象最终用户一样对自己做出区分?比如,有的事情在VB中能做,C#中就做不了。比如,对于VB来说,完全的晚绑定以非常简单的方式实现了,而C#中就没有晚绑定。为什么VB和C#有这样的不同?你们有意如此的吗?

Anders:我认为这个问题更多的是历史原因。我想说的是,我们必须考虑历史因素,尤其当你讨论VB时更是如此。VB有其悠久而丰富的历史,从一开始,VB就作为晚绑定的语言出现。(开始时)VB没有任何类型。很显然,晚绑定对于VB来说有某种核心作用。但是,从那时开始,VB已经逐步演进为一种更为“强类型”的语言,到现在,甚至你可以把VB看作一种支持晚绑定的强类型语言。呵呵。但实际上,这个过程是相反的。C#从一开始就是强类型语言,而且直到现在,我们都坚持早绑定。这并不是说我们在未来也不会支持晚绑定,但是,我们很可能以不同于VB的方式支持,而且可能对晚绑定的方式做些改进。C#是否支持晚绑定其实只是一种选择。对于老式的弱类型对象模型来说,比如OLE,如果我们从晚绑定角度出发,会比从早绑定角度出发好讨论得多,因为这种对象模型无非就是对象的若干方法的交互,反射,等等。

Charles:这些东西完全可以靠底层帮你完成……

Anders:是的,对,非常正确!

Herb:语言之间的差异在一定程度上是由用户引起的。对于靠近底层编程的C和C++程序员来说,性能永远都是一个核心和主要的问题。你可能发现不同语言有不同的特性,但是,更经常的是,你会发现这些不同特性想要解决的都是同一类的问题,比如,“并行执行”。现在,没有谁能够忽视这个问题,并且,一种语言如果想在未来5到10年保留在主流编程语言的队伍中,这个问题就是无法忽视的,因为这是硬件的发展方向。我们正处于一个新的时代,50年以来,我们首次在非单核的机器上工作。任何人都无法忽视这个现象。因此,就这个问题来说,大家都要处理一些相似的东西,但是,处理方式、语法可能不同,具体的特性也可能不尽相同。我也相信,不同语言推出同一特性的时间先后顺序也不相同,因为不同语言针对不同的客户群体服务,客户要求的东西不一样,因此,对于特性处理的时间先后顺序并不一致。就像Anders说的,各种情况都有一些。

Erik:是这样的。对VB和C#有怎样的差异,我可以给出一个具体的例子。该例子是“无名函数(或‘lambda表达式’)”。我们想在VB中也加入这种功能。首先就是寻找正确的语法。我们向VB项目组要到了VB的名称表,名称表中的名字支持两种语法的都有(VB和C#)。但是,这次他们想要更像关键字的名字,而不是C#那样长长的名字,因为他们觉得像关键字的名字更加“VB化”一些。这里你看到的就是语法上的区别。但是,在语义上也是有区别的。当你查看一个大函数内部的,嵌套很深的结构,比如“for”循环的时候,语言是何时、如何处理变量捕获,如何进行实例保护的就非常不同。在C#中,每次循环时实例都被保护,而在VB中,象JavaScript那样,变量是被隐性提升到函数顶部的。所以,在变量捕获方面,语义上的区别也存在。有时这些区别是极其细微的,你必须写非常变态的程序才能看到这些区别。

Anders:每次你写出依赖这样的特性的程序时,我们就能找出成百的Bug。呵呵

Erik:是啊是啊。

Brian:你逃不出作战室的
(译者注:微软的“作战室”,是产品、程序、测试人员一起对需求、找Bug之所在。)

Charles:这样看来,大家都同意不同语言在相互影响,不断演进。对于VB和C#来说,你们有相同的核心——处理引擎,你们必须在CLR的基础上出发,随着CLR的演进而演进。很显然,C++属于另一个世界。但是,各种语言要互相影响,你们必须在C#中加点什么来吸引用户,让他们用C#而不是VB.NET,是吧?应该不止是语法的区别,语言中必须还有一些核心的东西来吸引用户。

Herb:我认为你说的是对的。但是,我不同意你提出的理由,说我们必须在各自的语言中加点什么特性吸引用户,从而使他们不去使用其他的微软的语言。为什么呢?比如我吧,我更加关心使用C++或者C#的用户到底需要什么,我怎样才能帮助他们把工作完成得更好。也许某处有某种很牛的特性的语言,但我的工作是——怎样才能使客户的工作更成功?我必须要考虑客户会如何集成,我怎样做才能使客户工作得更好,这也是CLR的核心所在,因为目前已经不是靠一种语言就能做完整个项目的时代了。我怀疑在稍有点规模的实际项目中,是否还有人仅仅依靠一种开发语言。一般说来,你用脚本语言写点东西,其他语言写工具和组件,系统语言写核心的东西。你不停地在做集成。这就带来了我们所讨论的“可组合性”的问题。因为“可组合性”本质上就是跨语言产生的问题。当你写Web浏览器时,你不知道某个插件是用C#,C++,某种CLR扩展,还是其他什么写的。不管如何,这些东西必须一起工作,这就是主要挑战之所在。因为,要想使这种“可组合性”成为现实,我们必须时时将CLR和CLR以外的东西当作白盒来考虑。但是,我们这样做的时候又会碰到“锁”的问题。“并行执行”已经越来越重要了,但是,“锁”是完全不具备组合性的。因此,这是“可组合性”面对的主要障碍。我实际上已经转移到另一个话题上了。总之,对我而言,这更多的是一个语言交互的问题,而非语言竞争的问题。

Brian:我插句嘴。我在一定程度上代表了用户。我是个物理学家,同时,我也经常写点小程序,进行模拟和仿真,解决一些数学问题。要想成功,“可组合性”对我的来说是绝对地重要。我可以不在乎编程语言,但是我很在乎该语言是否有我所需要的组件。我有点夸张了,因为我其实还是在乎编程语言的,呵呵。基本上,我十分愿意使用任何能使我的工作更简单的编程语言。
这里,我先戴上顶“老人”帽,谈谈这个世界的历史上,非常少的成功软件之一——数值计算库(译者注:谢谢drdirac的修正)。这些东西是N年以前用FORTRAN写的。几十年以来,人们用这些库解决了许多非常重要的科学问题。任何头脑正常的人都不会想坐下来从头写一个“线性代数包”(译者注:谢谢drdirac的修正)或者类似的东西。有许多数学家终其一生在完善这些软件包。我们需要的是“互操作性”。不简单的是互操作性,我们需要的是“可组合性”。所有人都知道,FORTRAN不支持递归,因为所有的变量都是引用传递。这就带来了包之间接口问题。如果你想要集成某种自身内部不支持集成的东西,你就不能再需要集成的两边使用这样同一个包用于集成,这行不通。呃,我已经忘了最开始我在说啥了,哈哈,我尽讲些物理小故事了。让我回到C++、C#和VB上。这些语言我都要使用,我更喜欢C#一些,因为我喜欢它的操作符重载。为什么我喜欢操作符重载?因为我做数学计算,类似于四元数和八元数(译者注:谢谢pongba的修正)的奇怪线代运算,用一个小加号就能够代表那些要进行的一大堆计算。

Erik:伙计,也许你想用的是模板?哈哈。

Brian:(译者注:看样子生怕别人认为自己不知道模板)不,我才不想用模板呢。只要我一用模板,我就会开始想:喔,模板的预处理器是图灵完备的(译者注:谢谢drdirac的修正),也许我可以仅用(模板)就实现出一个链表处理库来(译者注:谢谢pongba的修正)……很快,我就会偏离真正的数学思考。在应用程序绝对需要晚绑定的场合(比如,那些小的计算模拟器什么的,晚绑定是成功的关键),此时,很自然地,我会选择VB。至于C++,天哪,大多数时候,C++用来实现其他的语言,做这类事C++很拿手。在用于科学的环境下,我多次实现过Scheme(译者注:谢谢drdirac的修正)。
总而言之,我就是泛泛谈谈“可组合性”。

Anders:如果你回过头去看看十年之前,会发觉潮流已经逐渐变化了。当我开始编程生涯时,进入编程这行的学习曲线就是:学习要使用的编程语言本身。各个编程语言几乎在每个方面都不相同。语法是你要学习的很大一部分。这是以前的事了。现在,你要学习巨大的框架,这个框架正越变越大,语法只是顶上的一小颗樱桃。我认为我们在这方面确实前进了很多。很有趣的是,编程语言就像你的眼镜一样,所有的东西根据编程语言的不同,要么看着是玫瑰色的,要么是紫色的,如此等等。但是,实际上起作用的东西是学习所有的API,学习你所基于的,越来越大的平台或者框架。如今,学习曲线的90%都耗费在这上面。掌握了这些,你就可以在C++、C#或者VB.NET什么的之间,毫不费力地进行语言转换,将部分项目使用这种语言,部分项目使用那种,并且找出组合这些语言的解决方案。相对于以前,实际上是不久之前,这是个主要的进步。当然,这些能出现,是由于有了通用的类型系统,以及各种语言中的那些抽象。每种语言之间的差别则是细微的,而且这些差别说不上来有什么特别的理由。

Brian:是的,在有的情况下,多种语言互相关联。比如,如今的Windows编程就是一项大苦差:你必须懂PHP、JavaScript、HTML、XML、SQL等等,要把这些东西全写到名片上,你就只有小小的一块地方可以写自己的名字了。哈哈哈。当然,能够同时使用多种语言也是有好处的,至少你可以选择自己喜欢的语法……

Erik:我们的编程语言之所以有差异,还是因为这些语言没有能够统一起来,在语言下面还有若干不一致的地方,我们实际上是被强迫使用不同的东西。CLR就不一样,基于CLR上面的东西使用相同的库,这些语言之间的排他性就要少一些,你可以选择,而非被迫使用某种特定的语言。

Brian:目前我们做得很多工作就是:减少大家被迫使用某种语言这种情况。我们努力改进平台,增加更多的功能,提供更多的.NET库。值得大家期待喔!

Charles:但是,像VB和C#这样的语言,C++除外啊,就如你们所说,它们确实绑定在某个框架上。这样的话,在一定意义上是否有其局限性?我的意思是,让我们谈谈函数型程序,这种程序如何能够融入到我们所谈的巨大的框架中呢?比如Haskell,有比如流行的F#,它们的结构(与现在的语言)完全不同。

Erik:很有趣的是,传统上,如果我们用“命令型语言”编程,我们的基本成份是“语句”。“语句”使用并且共享“状态”,从而导致不太好的“可组合性”。你不能拿着两段语句,然后简单地把它们粘合到一起,因为它们的全局状态不能很好地交互。这就导致“命令型语言”不能很好地组合到一起。如果你看看LINQ,就会发现我们已经更多地采用“函数型语言”的风格,所有的东西都基于表达式。“表达式”从其定义来说就是可组合的。你如何创建一个新的表达式?你用小的表达式组合出一个大的表达式,你使用lambda表达式,如此等等。从一定意义上来说,我认为在C#3和VB9中没有什么东西是Haskell或F#中没有的。这里面有一些深奥的事情,如果你看看Haskell的类型系统,你会发现这个类型系统跟踪程序的副作用。这就给了你一定形式的可组合性。现在你虽然不能把有某种副作用的语句组合到有其他副作用的语句上,但是,你可以组合副作用相同的东西。F#有一个非常强悍的类型推论机制,F#从设计之初就考虑了类型推论。我们以前也有类型推论,这并非什么新东西,但是现在的类型推论要考虑很多困难因素,比如,重载,这些东西使类型推论很困难。如果你从这个角度来看,我认为我们已经在很大程度上采用了浓厚的“函数型”风格,并且以相当“可组合”的方式来使用表达式和lambda表达式。

Anders:我想插进来说几句。我们对“函数型编程”的兴趣并非学院式兴趣。我们面临的一个挑战,嗯,实际上,当编程语言向前推进时,我们面临两类挑战。挑战之一是古老的追求——不断提高程序员的生产率,对吧?将采用和一直以来在采用的方法是——提升抽象的层次,对吧?给程序员垃圾回收机制、类型安全、异常处理,甚至是全新的“声明型”编程语言,如此等等。在提升抽象层次的过程中,正如Erik指出的,这些“声明型”语言获得了更高层次的“可组合型”。“函数型”语言之所以有魅力,正是因为你可以做出“没有副作用”,或者其他别的什么承诺,这样一来可组合性就极大地提高了。不光如此,在我们将如何让多核处理器、多CPU(比如,32个CPU)保持忙碌上,我们也会有所收获。显然,当我们更多地使用“函数型”或者“声明型”风格的编程时,我们更有可能把运行时框架构建得能更好地发挥多核的优势,更有可能更好地并行化。如果以“命令型”风格来工作,我们能够发挥的余地就很小,因为你无法预见所有动作——这拿点东西,那放点东西——背后可能带来的影响,所有这些必须串行执行,否则不可预料的事情就会发生。

Charles:这很有趣。我的意思是,作为程序员,使用了如此巨大的一个处理引擎——比如CLR之后,当然认为这些底层的东西应该被抽象掉。(译者注:Charles显然比较吃惊。)你的意思也是,如果我使用了一个4核的机器,运行时的引擎应该有能力负责分配进程(在CPU上的分配)。

Anders:嗯,你这样想很正常。但是,CLR以及我们的工业中目前绝大多数的运行时,都是“命令型”引擎,其指令集都是相当传统的,比如,堆栈增长啥的,以及拥有易变的状态,包括易变的全局状态等等。在此之上能够进行“函数型”编程,因为“函数型”编程从本质上来说,是“命令型”编程所具备的能力集的一个子集。现在我们想做的是最大化这种灵活性,但其实不过也就是让“函数型”能力子集越来越相关,使其越来越主流化而已。

Herb:我想,我们有必要在“函数型”编程领域做一个进一步区分,将其划分成两个部分。我非常同意Anders和Erik的意见。我不太同意的是这样的措辞:我们之所以继续使用“命令型”编程语言,是因为这是大家目前所能理解的;通用程序员目前的工作并未取得巨大的成功;市场对于“所有的东西都是表达式,所有的语言都应该是表达式类型的语言”这样的理念已经非常接受了;“函数型”语言是“串行执行”的好药方。我们要想使“函数型”语言运转良好,关键的并不是处理好基本的表达式问题,而是处理好lambda表达式和副作用的问题,关键是能够将表达式作为第一级的编程要素来使用——LINQ也是最近才在做,关键是能够指出lambda表达式和Closure(译者注:函数型编程语言中的一个概念,可以方便地组合函数,返回函数)的副作用。实际上,最后这点目前是缺失的(Anders也附和着:对,对)。这些东西在“命令型”语言中也是要处理的东西。我为什么提这些?因为我觉得说“函数型”语言是方向,目前的“命令型”语言不够好,因此是垃圾,必须要抛在脑后,全面采用“函数型”语言这样的说法不对(译者注:呵呵,对Anders的说法有点急了,毕竟是泡在C++上,对C++有感情的人)。我认为,对于“函数型”语言能够帮助程序员完成哪些工作,目前还不太明了。比如,能够用它写通用代码吗?能够用它系统级代码吗?当然,“函数型”语言有不少我们能够应用的好东西,比如lambda表达式,比如Closure,C#借鉴了,C++也在借鉴,这些语言因此增色不少。关于“函数型”语言还有另一个问题,那就是有两种类型的“函数型”语言,一种是没有副作用的,因此就没有共享的易变的状态的问题;一种是人人都在使用的,对吧(译者注:显然Herb认为“没有副作用”的理想情况是不太可能的)?因为你不太可能说,“瞧,我是完全并发安全的,因为每次我从XX(译者注:听不清)向量中得到一个拷贝,或者我使用XX(译者注:听不清)元素的时候,我都是取得一个拷贝”。确实不错,这里是没有共享的易变的状态,但是是否能够完全并发安全则不一定。

Anders:是的。我的意思是,在类似C#或VB这样的“命令型”编程语言中加入“函数型”结构,能给我们提供“以函数型风格”写库的能力,从而我们就能够非常明确地说,如果你能保证传入的lambda表达式是纯粹的函数,我们就能保证正确地把它分散到若干个线程或者CPU上,最后把它综合起来,给你一个正确的结果,我们能够保证代码运行得更快,同时你还不用作任何编码上的修改。如果你在写一个大大的For循环,我们永远都不可能保证做到前面所说的,此时,“函数型”编程能够提供给你的是一系列表达式,再加上“把代码当作参数传递”,“类型推论和泛型编程可以正确地绑定所有的类型”这些特性,这样你就能更方便地编写“可组合的算法块”。

Charles:这样一来不就削弱了抽象吗(译者注:Charles可能想的是程序员不需要再关心“可组合性”,语言和运行库应该保证这件事,而现在听起来并非如此)?

Herb:呃,我很同意Anders的意见,我想指出的是,当前所有的语言都有意不保证 “没有副作用”。之所以如此的原因是,除非所有的语言都添加一些机制让程序员可以清除副作用,我们这些做语言的人不敢打这个包票。但是,添加这样的机制涉及到众多参加者,大家一起思考、讨论什么是最好的方法的过程会很漫长。我们所做的是相信程序员,因为我们自己不知道。然而,程序员在很多情况下也不知道,因为他写的函数要调用其他的库。这里“可组合性”又浮上水面了,程序员根本不知道他用的库有怎样的副作用。一般说来程序员会再增加一层间接性,但是问题依然存在,没有人能够清楚地知道副作用,除非他拥有涉及到的所有的代码,这就是难题所在。上面这些讨论对“锁”也适用,因为“锁”也是个全局问题,对于“可操作性”是个障碍。

Brian:(译者注:在Herb说话的时候已经很着急地想说了几次)在这点上Haskell做得很好,Haskell是“永远没有副作用”的范例。

Erik:是的,但做到这点的过程也是痛苦的,因为并非所有的情况都一目了然。一旦你的(库)代码有副作用,而且因此使程序员的代码必须按照某种顺序执行(因为副作用的关系,该程序必须先干某事,再干某事),某种意义上你在用汇编语言编写东西,因为程序员将不再能用“表达式+表达式”的方式来写代码,他必须决定先对某个表达式求值,再对另一表达式求值,再把值加起来。因此我认为我们在这点上干得还是不够漂亮。

Brian:现在,我们在“流库”上有例子。好消息是,我们已经有Haskell向你展示如何以“可行性”方面的代价,换来用绝对纯粹的方式来做事。当然,除Haskell外我们有各种“杂牌”语言。呵呵!

(众人均乐)

Charles:这是个供研究的语言吗?

Brian:是的,我们将它设计为供研究用。

Anders:没有纯粹的好或坏,我认为,虽然进展缓慢,我们仍然快到一个令人满意的中间点了。我完全同意说,如果我们确实能够保证函数的纯粹性,生活将会非常美好。最终我们必须要做到。

Brian:在研究领域,大概有20多项工作与此有关——契约语言,契约和限制,等等。

Erik:但是,不少的副作用也并非坏事,如果我的函数使用了一个局部变量,这就是使用了一个状态,但是,函数本身还是纯粹的。如果你想要完全避免副作用,我觉得会非常困难,一些东西可以是局部不纯粹而整体纯粹的。

Herb:回过头,让我们从整体上看看“可组合性”。让我吃惊的一件事是,很多时候,人们甚至都没有意识到这是个问题。他们并没有意识到自己实际上经常碰到这个问题。整个软件工业,整个世界其实已经基于可组合的软件了。在硬件会议上,我经常对硬件公司提到的是(呵呵,通常此时我都是在轰击硬件工业,但是软件业也有同样的问题):硬件的并发问题被仔细地探索过了,而且,当前消除共享易变状态的最好办法就是“锁”;但是,锁是全局的,是一种全局资源,不能被组合;“被锁”是经常发生的事情,而拥有一个锁时,我还能调用任何其他的未知的代码,这就破坏了“可组合性”。说到这里,有的听者往往一脸茫然:这有什么问题吗?我于是指出,好的,你们是否上网下载别人刚刚发布的,自己喜欢的新软件,比如,某个浏览器,3个插件,然后就用呢?大家回答:是啊。于是我再指出,你们是否意识到了,当你们这样做时,经常地,这些软件都是第一次在最终用户的机器上被组合,被使用?既然如此,你们怎么可能对其进行测试?这时,屋子里有百分之十的人会露出恍然的表情,因为此前他们没有想过这个问题:这些软件是第一次在最终用户的机器上被组合,我们怎么进行测试?正因如此,“可组合性”是更加重要的一个问题。更不用说我们现在有AJAX,应用程序,以及众多的其他插件经常被下载,而且被要求在同一个用户界面中协调工作。

Charles:你这么一说,关于“函数型”编程,我马上想到一个问题是:在现有基础上再加一层必须考虑的抽象,实际上能不能增加程序员的生产率,是否真的有帮助?作为程序员,现在还要考虑“副作用”的问题。反正我现在用C#还是其他语言编程的时候,是不会像一个“函数型”程序员那样考虑副作用的。

Herb:往一个语言上增加更多的特性无法使其变简单,这是个我们面临的基本难题。

Anders:作为一个语言设计师,对于编程语言,我们所能做的是——减缓新特性不断累积的速度,从而避免最终的倒塌。我想说的是,你永远不能收回某个特性。理论上,你可以收回某个特性,实际中,你不能这样做,因为后果是若干代码的崩溃,这行不通。

Brian:是的,在从VB6到VB.NET的过程中,很多人已经感到饱受折磨了。是的,(微软的)解决方案是说那(VB.NET)是个完全不同的语言。但是,我认为,“函数型”编程的概念已经出现了不短的时间,新毕业的程序员至少听说过这个概念。他们在Python、JavaScript或者其他什么地方见过lambda表达式,或者他们学过Scheme,总之这不是个没听说过的东西,因此,当他们在C#或其他的更传统的编程语言,如VB或C++中看到这些概念的时候,他们并不会感到畏惧,因为他们了解这些概念的优点,他们知道这些东西如果用得正确,可以增加代码的可组合性。你知道,传统语言的可组合性靠的是程序员的自觉。在某些方面,Haskell编程比“命令型”编程要痛苦得多,然而反过来说,它也比“命令型”编程要简单些,因为你不会把事情弄得一团糟。呵呵呵。

Anders:这里我想插一句。我认为,在很大程度上,我希望我们在C#或VB上做的工作至少是——在很大程度上对“函数型”编程进行“去神秘化”。学院式的人总是倾向于让事情听起来比实际的要复杂,因为这样一来就显得他们自己比较聪明。“呃,我并不是针对任何人,但是,这里有不少符号代数和其他东西呢!(译者注:音频提高了,似乎在学某些人的语气)”。当我向人们解释lambda表示式时,大家是另一种感觉:“你的意思是他们就是函数?这有什么新鲜的?我们早就有函数了,lambda表达式只是些语法糖衣外壳吧?”是的,lambda表达式确实只是语法糖衣外壳,但这是种强有力的语法糖衣外壳,它使你能按照一种新的方式去思考问题,以前总是存在的一些语法噪音——必须声明函数,进行委托,等等——就会逐渐减少,最终,一个全新层次的表达式列表会产生出来。这才是关键所在!围绕着“函数型”编程还有非常多神秘,我希望我们能够(在打破神秘上)有所突破……

Herb:关于(Brian)说的教育问题,就是关于人们能够在学校中学到的东西的说法,我不太同意。我真的希望情况确实如此,但是我认为缺乏证据,或者说除了 “Pascal熟悉者”之外,我们无法获得更多。不管是在学校还是在今后的职业生涯中,人们都认为自己接触了多种语言,因为他们用过C、C#、C++、VB以及Pascal。但是,这些语言本质上是一样的,是同一族的语言。人们并没有接触过APL——编程就象在解释器外壳(译者注:可以想象DOS的命令行)上敲东西;不熟悉Prolog——任何东西都是规则;不知道Lisp——所有的都是链表。只有当你深入了2到3种这些东西后,你才知道,并不是所有的东西都是命令。也许在你今后的职业生涯中用的都是命令型语言,正如我们大多数人一样,但是,你不会对其他看待世界的观点一无所知。某人从小到大,如果没有离开过成长的城市,如纽约,超过一英里的话,会错过不少有趣的地方,不管你是否想在其他地方住下来,你应该知道它们,体验它们。

Brian:呃,我觉得这是个渐变的过程,当然,我确实观察到年轻一代程序员对于“函数型”编程有更好的认识了。你知道,10年,15年之前,如果你和人们谈起Scheme时,人们会感到不解地看着你,“什么?你在说什么?”。现在,人们听说过了,他们见过“函数型”编程的关键字。而且,通过JavaScript和Python,人们尝试了“函数型”编程,也使得这些东西更加流行。我并不是说已经出现了巨大的变化,我是说逐渐的变化和更多的认知,人们不再象以前那样看到这些东西就害怕了。

Erik:也许JavaScript是这个世界上最……(译者注:Brian一阵激动的插嘴,搞得Erik的话听不清了)。我不确定当人们使用JavaScript时,他们是否意识到了这是一种把“函数”当作第一要素的语言。

Brian:你可以在运行的时候创造它们(函数)……

Charles:所有的东西都是一种类型,所有的东西都是对象,是吧?这是个有趣的语言。随着整个互联网的发展,你认为这种语言也应该有所进化……?

Brian:这里的“所有的东西都是对象”,不是面向对象的意义。这里是从“任何东西都是关联链表”的角度来说。现在我听起来像一个老Lisp编程者(译者注:Lisp基于链表),是吧?一个JavaScript的对象不像一个C#对象那样——所有的内存排列都决定了,什么东西都静态地决定了。这意味着所有的东西都可以添加、拿走,可以违反规则,它确实只意味着一个关联链表。

Anders:属性包(我得意地笑)……

Brian:是的,属性包的说法更好!

Erik:是的,值可以是函数、其他对象,或者是其他属性包。

Brian:JavaScript其实就是源自Lisp的Scheme。我又在说Scheme,因为我也许是这个屋子中唯一老到听说过这个词的人,呵呵!但是,Scheme有点类似于“上帝的Lisp”(译者注:不知道是God’s Lisp, Guard’s Lisp还是什么别的,懂得有限,暂时按God’s Lisp处理,达人请指教),所有的噪音都被消除了,只剩下最基本的、最少的东西。JavaScript就是把这些东西带到了前台,披上件不那么可怕的外衣的结果。因为JavaScript看起来有点“C”的味道,你可以使用花括号!呵呵!

(一阵乱哄哄)

Charles:但是,JavaScript只是一种解释性语言,而且,对开发者来说,用它编程也不是很有效率。JavaScript不像C#或VB,它没有一种真正的面向对象语言所应该具备的IDE,以及你可以工作于其上的框架。

Anders:呃,JavaScript是一种弱类型语言,或者说动态编程语言。人们经常把“编译时”与语言的“类型强弱”相提并论。但是,这两个概念其实是相互独立的,是吧?你可以有一种“强类型”,然而在运行期编译的语言,如果你真想要这样的东西的话。但是,对我来说,我以前也讲过,我非常乐于向那些喜欢“动态语言”、“脚本语言”的人指出,正是他们所醉心的那些地方会有问题。经常地,人们都理所当然地认为,如果没有类型碍事,就不用声明什么东西了,可以随手就用,诸如此类。这样干的时候你确实能够更快地写程序,然而,这里有一个陷阱:因为没有类型,我们(编译器)给你提供的帮助就少得多。当你写“X.”时,对不起,“.”之后是什么我们没法告诉你,我们没有足够的智能显示给你,因为我们没有任何办法知道将发生什么。有时我们可以猜,但我们有可能猜错,有时我们根本就一无所知,对吧?X或者其他参数,我们不知道将发生什么。我发现,有趣的是,我们在C#中进行的类型推论的工作,在许多方面都使得C#编程看起来更像动态语言,因为不会随时看到类型,甚至压根看不到。但是,类型在那里,静态地在那里,这也意味着,我们仍然可以给你提供“语句完成”这样的智能帮助。

Charles:你是在说“var”关键字啰?

Anders:“var”关键字只是个例子。光看这个关键字,似乎这是动态类型,然而,这是静态类型。当你定义“var Blar”时,我们知道“Blar”是什么类型;当你写“Blar.”时,“.”之后我们可以为你显示出东西。

Herb:我们(C++)中也在做一样的事情。我有一个“int”类型的vector,我从该vector的头部取出一个元素,我有什么必要再用vector<int> 来声明一个遍历器?编译器知道它是这个类型,编译器能够将类型正确加到变量上。这就是“var”类型干的事,你不必到处都写出类型信息。这带来了很大的好处,你可以书写更好的“范型”代码,以前那些东西是写“范型”代码的障碍。

Anders:我想说的是,(如果有类型),我们就能在你写代码的时候给你许多的帮助,尤其在“智能感知”上我说是大实话。当今世界,如果你想把“智能感知”去掉,人们绝对会大叫“不!不!”,呵呵,这工具太有用了。但是,这里还有性能的问题。我是说,当编译器知道类型时,它能够为你生成更好的代码。就是说,你会得到更好的执行效率。

Erik:这里有件趣事。几个月前我们有次编译器XX(译者注:没听清),参加者都反映说,“喔,你知道,F#是最好的动态语言!”要我来说的话,F#是拥有最先进的静态类型系统的语言。当然,你不用写任何的类型,因为编译器帮你推断了所有的类型。很有趣的是,人们经常混淆“不用写类型”与“动态类型”。

Brian:这些人可都是写编译器的职业程序员!这是在课程结束时,做调查时出的趣事:“你最喜欢的动态语言?”“F#!”

Charles:但是(结巴了一阵,可能是震惊了),从开发人员的角度来看,动态类型是设计期的事吧?就我自己而言,如果我不必对任何东西进行强类型处理,我就会假设是动态类型。这是不是错了?我是说,编译器是怎么弄的?

Anders:“动态类型”和“隐式类型”是有区别的。一种情况是,靠编译器推断出类型,但编译器在编译期就推断出来了。另一种情况是,编译器在编译时一无所知,它假设这东西可以是任何类型,然后在运行时的适当时机,检查到底是什么类型。后一种情况下,当你分发一个虚函数调用时,也许会使用一个查找表,或是别的什么东西,因为你压根还不知道那是什么东西。前一种情况,根据到底是C#或是C++,你可以预先构造好虚表,准确地知道使用这个虚表的哪个内存地址来找到实际的东西,因为我已经计算好了。这就是预先有效地计算好了所有的东西。但是,人们往往错误理解(静态类型)为,“我,我这个人本身,必须在任何时候亲自手动写出类型”实际上这并不必要,因为类型可以被推断出来,如果你在一个地方知道了类型,你可以跟踪程序逻辑,根据这里发生了什么,类型是如何变化的,从而知道在另一个地方类型是什么。如果你是个足够聪明的编译器,你应该可以做到这些,是吧?我们正开始把这种“聪明”加入到程序语言中。

Brian:F#可能是这方面最好的例子。F#甚至欺骗了职业编译器程序员!我认为人们真正喜欢的是快速的反馈。人们喜欢快速看到结果。在他们的意识中,他们把这种喜好和“动态”相混淆了。下面我具体讲讲:你输入了一个函数,然后马上敲入一行代码来测试刚写的函数,如果能够正常工作,你就可以放心地忘掉它们(译者注:指可以投入下面的其他工作),这是人使自己的“大脑堆栈”保持满负荷运作的方式,是人的力量。历史上,“动态语言”在使人以这种方式工作上卓有成效。你打开一个Lisp,你实际上得到的是一个Lisp的监听器,你键入代码,然后在相同的环境中立即测试代码。但是,这实际上是个工具问题,与编译器什么的完全没有关系。你能够使动态语言有一个好的IDE和交互的开发环境,你也能使静态语言,如F#,拥有这样的IDE和交互的开发环境。这就是Sam在F#演示上干的事情。他打开好长一段代码,用鼠标选择它们,用“GO”按钮来执行;然后他打开另外一段代码,再用鼠标选择,再按“GO”来执行。所有的人都以为这是动态解释执行的。实际上,这是静态的,完全编译的。这就是欺骗行家的方法。呵呵(我得意地笑……)。但这确实是人们喜欢的东西。大家都喜欢能提供“不断反馈”的好工具,或者是“交互式开发环境”,无论你管这叫什么。只要你能够随时写代码并且测试,你就不必写一大堆代码后再测试。你会这样想,“嘿,我写了个叫‘RandomDouble’的函数,让我把它敲进去看看是否能工作”。如果能正常工作,你就可以在进一步的Debugging前把它忘掉(去做别的工作)。

(译者注:访谈到现在,众人已经很放松,谈话随意,插嘴较多,因此我加了比较多的句子补充和注解,利于理解。当然,这些是我自己的理解,可能是错误的,欢迎指正!)

Charles:但是在C#中做不到这样,你不能选择一些函数,然后就执行它们。

Anders:讲错台词了(译者注:Anders开玩笑,因为C#是微软的招牌,Anders暗指Charles这样讲不合适),实际上,这个东西我们也可以考虑一下(把它加到C#中),是的,这仅仅也只是工具方面的事情。

Herb:这是工具而已,从内部来说,实现它并没有什么障碍。这仅仅是工具的问题。你想要这东西吗?有投资吗?这东西对程序员重要吗?符合这种语言的侧重点吗?要考虑的是这些问题。

Anders:当然,动态语言已经显示出这是个重要的工具了。

Brian:之所以动态语言往往和这些交互的东西相联系,这完全是个历史偶然事件。APL可以说是所有这些东西的母亲。键入“/”、“←”、“(”、然后直接执行!哈哈哈!你知道,这些东西也是可以静态编译的。

Charles:让我们稍微谈谈。对我而言,语言革命的发展方向似乎是“命令型”和“函数型”的结合,对吗?

Anders:动态和静态的结合,说实在的,我认为主流是融合各个领域的特点。经典的、面向对象的、函数型的、动态的,你知道,我们从所有这些吸收可取之处,比起以前,生硬地嵌入(另一种语言的东西)将越来越不可取了。

Charles:你认为,这些东西综合起来对程序员生产率产生的影响,是否为正值?或者,对于那些如Herb所说,没有能够在20种语言上进行实验的程序员来说,这些将发生在C#和VB.NET上的事情将是奇怪和难以掌握的?这个世界突然要求更多地考虑副作用;语法、编程环境和程序院以前所熟知的也大为不同。当他们被带到这样的世界时会如何?我知道你们已经在LINQ上做了很棒的工作,但是,LINQ和C#程序员所熟知的环境还是不太一样。未来将会发生什么?

Anders:呃,(生产率)公式其实很简单,我是说,当你加入新的语言特性,新的功能的时候,你必须要谨慎地考虑对学习曲线的两端——熟手和生手——的影响。也许生产率增加了。也许你现在只需要10行代码就能搞定以前要100行代码的东西。是的,你需要学习如何写这10行代码,但是,嗨,一旦你学会了,你就可以不断地写更多的10行,你的生产率会提高。这是个经典的公式。

Charles:而且这些东西的可组合性也更好……

Herb:最终,所有代码应该统一。现在,你可以用鼠标选中一些代码,然后执行。然而,有的(新)东西你能很快掌握,有的东西就需要进一步学习了,这是语言必然带来的问题。大家关心的是,我们怎样才能使某些(新)东西和现存语言的相应东西尽量相似,尽量吻合;使现有语言的概念和(新)概念能够协同工作,反之亦然。这样做了,我们才不会出现如下场面:嗯,这里是C#3,C#3支持硬嵌入其他语言的代码,这些代码不和C#3协同,但是它们和C#3使用同一个编译器,你可以在C#3中用不同的代码段硬嵌入它们。你肯定不希望出现这样的场面,任何头脑健全的人都不希望。因此,问题就是你怎样才能更好的集成,这点才是我们经常面临的挑战。

Brian:这里就体现出LINQ的美妙了,因为LINQ可以让你逐渐过渡。一开始你有遍历器和“For…Each”语句,然后你可以使用新的,与SQL语句更加类似的“For”语法。这是个渐进的过程,一步步来,慢慢学。要获得LINQ给你提供的好处,你不必要一下就使用全套的“函数型”编程利器,搞一击必杀,你可以慢慢来。

Anders:呃,对我来说,价值所在是:我们在非常非常实际的问题——查询、数据获取、消除数据领域和通用编程领域之间进行映射困难——上应用了“函数型”编程的原则。你知道,这些是90%C#用户每天都在做的事情,很明显,我们在这里的工作非常有价值。

Herb:同样的事情也在“并发性”上发生。如果你用了些新的“硬嵌入”特性,也就是说,你写了一些并发的代码,用了不能与其他代码协同工作的特性,你就是在自求失败,没有人会发布这样的库,人们会一直等下去,最终你发布不出来任何东西。因此,没有人会这样做。另一方面,你会想,我怎样才能加一些东西,从而使我自己能够从一些一直在做,但一直很痛苦地用手工做的事情中解脱出来。这就是要用一些抽象层来帮自己。我喜欢用“对象”来举例。现在,每个人都在“对象”上工作,“对象”已经成了人所共知,非常俗的一个词了,难道还有谁不知道虚函数是什么东西吗?但是,20年以前,我们参加那些“深入讨论……”之类的研讨会时,人们热烈讨论“什么是对象”,“什么是虚函数”,针对这些问题,一本又一本质量参差不齐的书不断地写出来。实际上,这个所谓的“对象”并非什么全新的东西,在这个概念出现之前,人们已经用C写了15年的面向对象的代码了。看看C的静态库,“fopen”为你创建了一个句柄;然后你调用“ftell”,将这个句柄当作显式传递的this指针;然后你调用“fclose”,相当于调用析构函数。因此,没有什么太新的东西。那么,为什么人们要用“类”来代替这一切呢?因为我不想再用手写虚表了,谢谢你,我有其他更加重要的事情要做。因此,这些抽象是为了处理人们已经在做的事情,而且,在一定程度上,这是检验我们的设计是否良好的标尺——当你用这些抽象来处理已经在做的事情时,是更加痛苦了,还是简单了?以上的讨论当然适用于关于“并发”的抽象,因为,手动处理线程和锁,与写C代码并无二致。用抽象层来处理这些老的并发问题时,应该使工作更容易;我们也谈到了“可组合性”,抽象层也应该使“可组合性”更简单。LINQ实际上同时处理了几个问题,因为可组合性往往与并发紧密相连。比如,“我怎样才能在同一个地址空间中组合这两个东西,让它们同时运行?”就是个与多方面相关的问题。因为LINQ内生的抽象性,它关注的是要做什么,而不是如何去做的细节,这就使“运行时”可以接管调度和分配(比如,在4个、8个CPU上协同一个EXE)的工作。不管硬件如何,“运行时”负责使程序运转良好,程序员完全不用作这方面的决策。现在我们手动做这些事,是停止“手工写虚表”的时候了,但是,我们需要提供更好的工具,这样人们才能真正放弃手工操作,转而使用抽象层,用10行代码干完以前100行代玛干的事。
(译者注:Herb一说就是长篇大论,到后面说高兴了,似乎已经忘记前面关于程序员要考虑副作用的事了。)

Erik:这是很重要的一点。我认为,作为语言的设计者,我们在“计算机帮助我们编程”上做得还不够好,因此我们才有很多东西需要手动做。看看在类型推断上我们做的事情,对于那些我们已经掌握的关于程序的信息,我们用计算机的力量来代替手动寻找。如果你想要“并发性”,如果你想要把程序语言设计得可以使用工具,使用计算机来帮忙获得更好的“并发性”,我认为我们可以做得更多。我们实际上可以把工具设计得可以互相帮忙,这样就可以加速前进。我考虑过很多东西,甚至“运行时”,因为我们有元数据,因此我们现在可以进行垃圾回收,以及进行其他处理。对我来说,这就是趋势所在:你如何设计程序语言、编程环境,从而其他程序可以“钩入”,帮助你做事情。从一定意义上来说,对“非托管代码”,工具就不太能帮上忙了,因为没有足够的内部结构(让其他工具可以钩入)。我认为,这是驱动很多东西的因素,我们今天谈论的很多东西都可以从这个角度来审视。

Charles:我想问个问题:多少抽象才算多?你们在抽象的路上能走多远?我的意思是,在某点上,有可能不用写代码了吗?

Anders:在抽象问题上,我通常看到的问题是:上移抽象层次,与增加抽象层次是有区别的。我认为,通常说来,上移抽象层次是一种罪恶。上移抽象层次意味着我们在“美妙的工作流商业概念层”,或其他类似层次上编程,对吧?如果想往下靠靠,呃,很不幸,现在你不能调用方法,或者写表达式,或者做其他以前你能够做的事情了。因此,你得到一些,失去一些,总体来说,伙计,有时候你干不了事。我认为重要的是增加抽象层次。你不能拿走底层的东西。是的,我们可以谈工作流、规则,以及其他的东西,比如,在JDE(译者注:Job Description Environment?工作描述环境?)中声明东西。但是,你仍然可以到底层去,写那么两行代码,从而省掉一天的时间,对吧?你不用经常到底层去,但是救生口在那里。对我而言,实际上,是“抽象的范围”(译者注:抽象覆盖了多少个层次,也就是说有多少层抽象)体现了工具的能力,而非抽象的层次(译者注:在多高的层次上抽象,因为光追求高层次抽象就是把抽象层次上移),如果你一直在听我说的话(你应该知道我的意思)。

Herb:当然,说的太对了。这和现在我们写一个库的情况是一码事。我们写一个函数,用名字调用它,因此我们就不必每次都写上几百行代码了。这个事情(是否写库)可以由程序员自行决定,大量编写(译者注:这相当于增加抽象层次)。但是,谈到语言特性时,有时,你不指望程序员能为你写出另一部分编译器(译者注:Herb指由应用程序开发者来实现某些语言特性。微软的Phoenix项目将提供这样的框架,可以由其他的程序员来实现部分的编译器。呵呵),你希望由语言来帮助你,由工具来帮助你(译者注:这相当于抽象层次上移)。界限基本上就是:库和语言。什么地方真的需要工具的帮助?因为工具不知道(很多具体事情),而工具影响代码产生的方式,然后,你就不能仅仅依靠程序员写出更好的类、更好的框架(因为人们也能自行决定是否编写框架)或其他什么来解决问题了。这些东西将使协同变得很复杂,很难理解。
(译者注:看起来Herb不是很赞成使用很多工具来帮助编程,不知道理解得对不对)

Erik:呃,从定义上来讲,我认为不可能有过多的抽象,因为抽象意味着剔除不必要的细节。因此,如果细节不是必要的,你就可以剔除它们。但是,我觉得……(译者注:场面混乱,噪音陡涨,众人都纷纷对这个意外的角度表示感兴趣)。有时候你会把必要的细节抽象出去了,因此你得不到这些必要的东西了,此时抽象就走得太远了。但是根据我的定义,你不是真的在抽象,因为你剔除了必要的细节。

Brian:我们管这叫“分散”。

Charles:那这就是说……,呃,好像有人要用这个会议室?

Anders:是的,我们要被赶走了。

Charles:我们还有一分钟。刚才那个论点很棒,我是说,抽象的层次与仅仅上移抽象(Anders插嘴:抽象的范围和抽象的层次)。但是,有的东西在CLR就是很难得到,比如,当我写一个完全托管的程序时,如果不用那个相当复杂的P/Invoke模型,要得到一些相当底层的结构,是相当困难的。

Anders:是的!但是,想象一下这样的世界:你没有P/Invoke!我是说,这实际上是个把抽象上移的例子。然而,救生口(P/Invoke模型)在那儿,当然,我们现在竭尽所能,使你根本不必动用这个救生口,但是,如果你必须要去那里,你可以去,对吧?

Herb:对你自己而言,把救生口焊死是非常不利的。

Anders:是的。因为,在某些情况下,总有些傻傻的原因会使你从箱子上跌落下来,对吧?

Charles:好的。在走之前,让我们轮流说说语言的发展方向。我没有别的意思,只是想让大家谈谈,就不断演化的语言来说,我们正在向何处去?快速地轮流说说,告诉我你认为将来是怎样的,比如,5年后会如何?对你来说,在你的语言中,向开发者提供什么是重要的?怎么样?
(译者注:Charles明显有语无伦次的嫌疑。估计累了……)

Anders:我想说,好像从我开始哈(译者注:Anders坐在桌子一头,Herb坐在另一头,Anders率先开说,突然之间觉得不妥,故此加了这一句)。我们已经看到了语言特性的融合,对吧?对我来说,这就是主流。但是,关于将要解决的主要问题(正出现在地平线上),我想是:我们如何处理多核?更好的并发编程模型是什么?只是个非常简单的概括。

Brian:我以前的模型是:让数学家成为更好的程序员。我现在的模型是:让程序员成为更好的数学家。我希望编程语言看起来更像数学。
(译者注:窃以为Brian现在的模型不符合软件工业化的要求)

Erik:我想用工具来增进人的智能。计算机比我们的能力要强大得多,我希望计算机能够帮助我编程,我希望我的程序能够设计得使这种帮助成为可能。

Herb:大家说了很多了。是的,从其他语言借鉴,尤其是把“函数型”风格植入“命令型”语言,是个趋势。这个趋势不难看出,而且在未来几年都将保持(译者注:Herb在这里开了个什么玩笑,没有听清)。另外就是,能够讨论并发问题,尤其是在线程和锁之上增加抽象层次。很多人在解决这个问题,事务内存、交互式对象,等等。LINQ在这里很有希望,LINQ在非并发领域已经做了很多好工作,这些工作能够直接应用到并发领域。所以,我们正在各方面不断推进,并且把所有工作整合到一起。

Charles:很棒!会议室到时间了。访谈很棒,谢谢大家。我希望能够和大家再次交谈,如果你们关于编程语言的演进有什么想法,欢迎联系我。谢谢大家的宝贵时间!
您需要登录后才可以回帖 登录 | 申请新用户

本版积分规则

小黑屋|手机版|Archiver|守望轩 ( 湘ICP备17013730号-2 )|网站地图

GMT+8, 2018-10-21 07:54 , Processed in 0.079551 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表