BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

Simon探讨编程语言与研究工作
录制于:

| 受访者 Simon Peyton Jones 关注 0 他的粉丝 作者 Sadek Drobi 关注 1 他的粉丝 发布于 2009年7月18日 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。
42:53

个人简介 身为格拉斯哥大学计算机科学系的荣誉教授,Simon Peyton Jones目前工作于微软剑桥研究院。他领导着几个研究项目,专注于函数式编程语言的实现和运用。他对于Haskell语言的设计有着突出的贡献,并且是格拉斯哥Haskell编译器(Glasgow Haskell Compliler-GHC)的首席设计师。

   

1. 和我在一起的是Simon Peyton Jones,我们将对编程语言作总体的探讨,特别是Haskell。Simon,跟我们介绍下你自己,说说最近都在忙些什么吧?

我任职于微软剑桥研究院。我已在那里工作10年了。在此之前我是大学计算机教授。我转向微软以追求变化,因此感觉上我像是大学里面休假的人员,区别是一直都在休假。我得以能够做我自己喜欢的事,而我喜欢的事就是研习函数式编程语言,因为我乐衷于让计算机变得更易编程。此刻,我认为函数式语言至少提供了一个重要的方式使得计算机最终能得以更容易地编程。我所做的一切都跟一种叫Haskell的语言有关。Haskell并不是我直接研究的对象,而是我其它研究的基石。我们拥有Haskell,这一编程语言,与在Marlow的同事-我们为Haskell构建了名为GHC的编译器-以及许许多多使用它的人们一道。现在我可以研究一些新的编程语言特性,比如事务性内存。我们将其融入了GHC并加以精心打造,在下一版本发布中,许多人就可以开始使用它了;我们得到了很多良好的反馈。对于研究编程语言特性如何工作来说,这是一个非常好的反馈。

   

2. 前一位微软研究员(指QCon2008上)试图找到一种多范型的语言,其本身实际上是函数式编程语言,同时也是带有某种改变的对象编程语言,比如F#和Scala;你能对此详细阐释一下吗?这能够解决我们目前主流环境中所面临的问题吗?

我认为两种不同的事物将继续:一是你可以从所熟悉的主流语言开始,其本质是面向对象编程,你可以尝试将来自函数式编程的优秀元素添加进去,通常是并发,这将把你带入一个良好的方向。这正是F#所做的,而在一定程度上C#本身也与之一样,逐渐发展着许多受函数式语言启发而来的特性。但不管怎样,你所做的始终都是一个折衷。从一开始,我们并不拿走什么东西,我们只是添加。

这使得整个企业更加复杂-这本就是一个够繁杂的开始了-而如果把事物隔离开来会更容易研究,因此我将那里所发生的看作是我们正处在编程世界的引力中心,也正是像我这样的人所做的。让我们采用一个更原则性的方式:特别的就Haskell而言这意味着对于纯粹性的严肃-没有副作用-,这并不是因为它能够解决目前所有的问题,而是因为它值得人们来研究,就像在一个实验室的条件下一样。我用刚才我所作的关于交互虚拟化的报告里的一个例子来说明我们如何采取这一方式:我从主流世界里获取了事务性内存,在Haskell的世界里研究它并从中领悟到新的东西,又可以移植回去。我不认为这一方式是相互竞争,谁比谁更好,而是将其看作相互补充并且最终都是朝着同一个目的地。

   

3. 举例,C#加入了一些函数式编程的结构;你认为语法重要吗?你用C#语法进行函数式编程,不会觉得这会有什么影响吗?就好像是让程序员们以命令式编程的文化来编程,你看,他们由C#获得了这一语法。

习惯性的来说语法在编程语言里并不特别重要,你一样能解决它,但我认为实际上语法是对一个编程语言接口的运用。因此它有着长期的并且普遍的作用,我认为。当然,向C#这样本就有着非常完整的语法结构空间的语言添加新的特性,意味着你可能不能完全按照你选择的方式来做所有的事情。实际上,我认为这已经解决得很好-他们为LinQ和lambda表达式所选择的语法。除语法外,这个问题言外之意可能是“它究竟能如何方便的编写出显著的程序块。而不只是一点点函数式的小玩意儿而已?”。

在C#里没有什么lambda表达式,而是与特定的LinQ相关,当你说:“按照这一预测,用这个布尔表达式过滤这一记录”,这就会非常有用。但这些lambda表达式非常小,通常都是一行,所以要将C#发展为你可以仅用函数编写整个模块的语言,这并非是一个合适的载体。

这比语法意味着更多的东西,这是函数应用。如果你拿一个函数赋值的事物,与一个值,哪怕是做一个应用-不只是将它们每个挨个放在一起-你必须都得有一个应用操作符。因为C#要同时完成很多事情,你也不能怪它,它做了很多像Haskell这样的语言没有做的事情,但如果我们试图同时做很多事情而不是任何特定的事情,这就显得不是那么方便了。是否是这样,反过来,人们对函数式编程的理解和感受可能会有偏见,比如他们会想“这也太笨拙了”,我希望不是的,我希望他们去想“这非常酷,我也许应该试一下纯粹的形式。”

   

4. 使用像C#或Java这样的语言我们创作对象,我们有着创作对象的模式。在函数式编程中我们创作函数。你对此有什么想法?同样的事情由两种范式来完成:在对象中你需要创建许多对象,在函数中你只需创作monads和其它的事物。你对此怎么看呢?

我并不是一个经验丰富的面向对象程序员,因此我不认为我对用面向对象语言创作大规模的对象有一个宏观的蓝图。我想你谈到的是设计模式。设计模式是对你如何组成对象的方式的一种非正规的描述。这种非正式性有其优势在于能给予它们更广泛的应用,但同时也有其弱势,因为它不能作为一个库来验证,也不是对象;它更像是你脑海当中的一种勾划。

我经常思考设计模式在函数式语言中对应的是什么呢,因为不存在像在面向对象编程中那样的相应的函数式语言的设计模式书籍,这是为什么。因此我也没有一个绝对的回答。我想其中一个答案可能是很多组成函数的方式都可以被表达成高阶的函数,它们是将函数作为参数的函数并将它们结合在一起。它们只是更多的程序。这种意义下,我并不认为它们是什么不同的事物,它们只是更多的函数而已,但实际上它们的角色是将更多的函数粘合在一起。

让我们来看看这个例子如何:将一个把函数应用于列表的每个元素的函数映射到一个数组或集合,就是这样的例子,因为它将函数作为它的参数。但在大规模的编程中,你趋于获得更大规模的高阶函数。我不知道高阶函数是否真的囊括了面向对象程序员所有对于设计模式的看法。我仍在等待某天有位达人创作一本函数式编程的书名字叫“函数式设计模式”。

   

5. 你能给我们详细的阐释一下你对C#的列表内涵式(List Comprehension)实现有何看法吗?它不是一个语言集成的查询,实际上是一个集成于语言的monads,因为它将monads引入了C#。

这是LinQ的聪明之处;他们本可以做的是,只需说:“获得SQL并将它丢进语言里并且只做这一件事”。但他们更聪明,他们找到了方法对任意的迭代器做 Sequel一样的查询,因此能产生一个值流。而且他将其实现为可扩展的,因此你可以加入新的迭代器,可插入,并且能尽量发挥基本的LinQ框架,因为他们让你访问那些表达式树。说实话,Erik是LinQ的专家,但如果说它是由“让我们来处理Sequel查询”这样的具体例子而概括出来的,是非常正确的。我相信在开发的过程中像Erik这样熟知monad的人一定会说到:“我们知道如何来概括这个”。

他可以从Haskell和其它相似语言中获取这一更加一般化的框架,将其运用于C#的上下文并且在他们所能做的一般性的层面上产生重要影响。我想如果不是这一输入我们看到的可能会比现在出来的缺乏灵活。正在实现的和被设计为要实现的都是特殊地C#化的。它们是精心设计的,如果没有认真的使用过它是无法理解它的所有分支的。但你能看到其直接的联系,而这也是他们今天所承认的。

   

6. LINQ是列表内涵式的概念之一;除了列表外你认为还有其它的monads或其它的抽象可以从Haskell获得而被实现到主流语言当中吗?

部分而言主要的影响是简便性。在函数式编程当中构建数据结构并进行模式匹配是很方便的,而且你可以用一些与此同形的方法来达到,而在面向对象语言里,却没这么方便了。并不是方言式的语法使人气馁;而是你必须写太多的东西了。这让你对特定的编程模式心灰意冷。我不知道慢慢的加入一些语法的支持是否会带来不同;或许是可能的。Scala已经离此不远了,我认为。所以我想会是一个语法的模式匹配,贯穿函数式编程的早期事物(被加进到主流语言当中)。

今天早上我谈到关于作用和纯粹性的负担,我认为最终从一个纯函数世界能进入主流的最重要的事物将是约束子程序或者函数过程所带来的作用这一思想。或许只需说它是完全纯粹的,但你仍会想要限制它的作用-如果你牵扯到事务性内存的话-这样过程仅仅能读和写事务性的变量;它不能执行IO,也不能读和写非事务性的变量。

当你有了这些保证,如果它被静态地保证,那么实现最佳方式就变得非常容易了。我猜想未来我们将会在主流语言中看到一些限制作用的方式;我不知道它看起来将会是什么样,但有太多的理由能说明它为何是如此重要了。

   

7. 你认为tree monads,或continuation,或其它类型的monads能在C#里得以实现吗?你是否认为这对业界是有益的?

这里你所指的是,在Haskell当中,monad的想法通常一开始是以输入/输出的形式出现于人们的程序中。我们同样探讨过了事务性monad,但实际上Haskell里并未内建任何monads相关的事物;编译器里不存在说“我知道这是一个monad”,除了对"do"标记的一点点语法优惠。但这真正的意义是程序员可以自己定义新的monads以及你所提到的一系列的continuations传递;另外一个是封装状态。

封装状态意思是,有时候你要做像排序算法这样的计算,其外表上是一个纯函数。它获取-让我们假设-一个数组并产生排好序的数组。内部而言,它可能有很多的副作用,但它的外部接口是完全纯粹的因此你不能说它的实现是使用了副作用。那么你应当如何封闭才能让它的内部命令式对外部是不可见的并且得以静态地保证?其结果是,使用这一monadic构架你在像Haskell这样的语言里不需要对编译器作任何扩展。

我所说的是,比任何特定monad更重要的可能是,你可以定义新的monads或者你自己的monad变换器这一事实。这意味着-对于任何特定的应用程序-如果你想你的monad适合于你的程序,你需要裁剪它。这将会是在C#这样的语言里非常乐于见到的一种功能,但我认为会有人相当怀疑并说:“你如何获得这些精致的抽象事物?”为了拥有monads你需要拥有高阶的类型变量,比如像在普通的语言里,你可以说“list of T”;T可以是指代int类型或float类型或者是int类型的列表的一个变量。所以T指代的是“类型”。我们同样希望T可以提供类型构造器,这样我们可以有T的列表以及T的数组,可能你还想要一些M,它可能会成为一个列表或者数组。

所以现在M成为了这样一种事物,它获得一个类型,并交付一个类型,它是一个类型到类型的函数,如果你高兴,它可以是一个对此抽象的变量。而这是你的.Net所完全没有的。很重要的一点是monads在Haskell里是一般化的。我有一点还不是特别肯定的是,如何将它改装而不用给.NET添加高阶的类型变量。也许迟早都是要加进去的,谁知道呢?但有些情况你无法仅仅把新东西塞进一种语言。这会有范型的冲突而你只会得到一团糟。

我不知道在C#中我们是否已经达到了那个点。你问题的趋势是:我们能一直举出东西来并把它加到那里吗?但某种时候我猜这样做是不会满意的,这并不是对主流编程的批评,这只是因为它们是为不同的目的而设计的。

   

8. 我们有Lisp和Smalltalk这样的语言,只有最少的语法;而我们也有C++,C#等语言,有着很多的语法,那么你认为Haskell是处于中间什么位置,你就此有什么看法?语法是增加了语言的复杂性还是为开发者减少了复杂性呢?

这是使用接口问题的又一例子,不是吗?我认为语法有两种味道:有一些是表面的,在Haskell时我可以如此定义函数,ƒ x=let y=x+1 in y * y ,但我同样可以这样定义同一个函数 ƒ x= y * y where y=x+1。这只是语法本身而已,我只是用"where"替代了"let";只有细小的差别:"let"表达式在任何地方都会发生,而"where" 只在所附加的定义中产生,然而,这也只是表面上的差别。

当我们一开始设计Haskell的时候对于“所有的事情我们都应当只有一种方式来做”产生了很大的争论,实际上最终我们实现的语言提供了多种语法来达到同一件事情,因为当你写程序时,有时候"let"在某些情况表达了你想表达的东西,而有时候用"where"表达恰到好处。我认为这只是表面上的复杂性,因为对于程序员来说理解这两者从智力上来说都不复杂。你展现了,这就对了,你不必要求更多。

有些问题要复杂得多,比如列表内涵式。在Haskell当中你像这样就完全将列表表达出来了[y*y |y?yy, y > 3]。这种类型的内涵式已经在像Python和Ruby这样的语言中逐渐显现。因此对于其意义还有更多需要解释的。实际上,我最近将其概括成为更SQL化了,因此你同样可以以列表内涵式的方式来表达group by和sort by语句。但它们仍有些流于表面了,更深入的东西在此。一个更深层语法问题的例子可能是类型类。Haskell的类型类跟面向对象的类极其不同,然而你仍可以说它们不过是语法的类声明,实例声明,但这实际上这只是你需要理解的一系列的概念集合当中最浅显的一部分罢了。因此我试图将表面的语法与深层次要素加以区别。

我对表面的语法比较随意,而对于深层次要素,Haskell有着类型类这一强大的概念使得许多编程都变得容易了。但无庸质疑的是,如果你要用某种特定语言来编写程序,你必须要习惯于它,这对于面向对象的类而言也是一样的。

Smalltalk和Lisp都有属性这一概念,但它们不仅语法更为得体,还有幸节省了许多概念,因为Smalltalk仅仅是对象和消息,只需S表达式和lambda来捕获自由的变量。对于深层次要素,它们更是尤为经济,而它们选择的要素使其非常强大。但这里也有一个交换条件,因为Lisp的 lambda演算非常强大,但要表达任何特别的事物。你可能会需要很多的圆括号。这也被称作:“很多烦人的过多的括号”。

所以你会说:“那让我们来大大简化一下我们想做的事吧”,这就是表面复杂性的角色。我实际上比较支持一个中等的富语法集。Lisp所带给你的,而当你拥有了富有的语法之后却会变得困难的,是Lisp中程序本身就是一个可以被Lisp程序操作的数据结构,这就使得元编程非常方便。但Haskell有着相对丰富的语法,用微软的话说:这一类的代码“非常复杂并难于理解”,所以当人们提到“丰富”的时候,请总是保持怀疑,所以丰富意味着由许多构件,但我以为最大的不同无非是表面层次的。然而,如果你想要编写操作Haskell程序的程序,那你需要面对许多语法变种。

这就是交换条件-这会显得很为难,因为元编程对于编写特定类型的程序非常方便,但这意味着所有的Lisp开发者都不得不使用这一小语言。我想这就是所谓的交换条件吧。但以C++为例,它极其复杂;它有很多深层次的复杂性。我不认为我全面的理解了C++。Haskell也有着一定的(复杂性),以示公平,它还在不停的增长。我建议“专注于深层次复杂性而不是表面的语法”。

   

9. 函数式语言的想法让我有点害怕。基本上,如果你思考对象,当你对一个给定的对象拥有它的方法和属性时,它可以映射到现实的世界中,如你可以说“一个人.用手.挠痒(另一只手)”。当你对一个给定的对象拥有它的方法和属性时。而对于接受函数式编程和以函数的方式编程,看起来似乎是需要对范型的转换,以及对于整个思考过程的转换。你认为面向对象语言成为主流的原因之一是什么呢?有没有什么方法可以填补函数式和面向对象之间的间隙?

我想,毫无疑问,从整体上而言命令式编程语言,特别是面向对象的语言,对于操作的模型有着非常好的映射。你可以想像这些位置是如何变化以及程序是如何一步一步的执行的。这实际上是近距离的观察在底层真正的机器上发生了什么。而我们对于此有非常良好的可操作性理解。很多使用电子表格的人们并不认为自己是程序员;他们认为这是建模。而你如果不觉得这是一个步骤的序列的话,你也不并向他们解释了。我在之前的讲话试图表达这一观点。

试图将计算当作处理值的函数来对待,以函数式编程的方式来表达这一思想,其实是非常自然的。如果你一个熟练的面向对象程序员试图在半路出家学习像 Haskell和Lisp,其思维过程确实截然不同,很多你所熟悉的习惯可能不会很好的工作。我花了一个上午去学习单板滑雪的课程,而我本人是一个中级的双板滑雪者;我总是不断的跌倒,因为我的本能总是完全错误的。我做出的每个本能的动作都会让我跌倒。我放弃了,还是回到了双板滑雪。

这里就好像是这样的一种感觉,初看起来不光是笨拙并且还让人精疲力尽。你这样做又是为什么呢?你学习这门语言,而我们不知道如果你早一些学习它的话会发生什么。现在越来越多的毕业生走上社会时或多或少的接触过一点函数式编程,我认为这是一件很好的事情。但我要说的是:我希望看到这样一种经验的重复,尝试这一试验的人们会说:“现在我将把我的自由理念放到一边,投入这个世界,好好尝试一下并坚持下去,得到一种惊喜”。而人们常常说的是:“现在我要回到我的 Java程序去了并尝试用一种不同的方式来编写它们。”

我换了一种方式来思考它们。所以我不得不让思想重新组织,但有一些重新的组织却让我受益终生。为什么呢?我想这是因为所能发生的是你可以在Java里做函数式编程了;你尝试更多的运用不可变的对象;给予你一个String,你不是改变或胡搞一通,而是把它拷贝给另一个,这样你仍可以拥有原始的那个,而且你的程序交互更少了,你发现你越来越多的进行这种尝试。你会想:“这是否已足够了?” 实际上,现代来说这可能意味的是:良好的垃圾回收器,与良好的存储分配器。

我作为一个编译器开发者,所做的一切目的都是让这些分配和事物更快的运转。这些函数式编程的习惯可以回到命令式的世界里,我在我的讲话里试图说明其理由。我认为那些对于计算内部而言是附带发生的,而对于其外部行为并不重要的副作用,如果能在编码时尽量避免它们,就像它们是偶然的,去掉那些你可以不必需要的副作用,这将改变你的整个编程模式。

但你仍可以在命令式语言中做到这些,这样你最终得到的程序就拥有了我试图描述的一些好处。底线是,这是一个思想重新组织的经历,并且无可逃避,这不像是从Java转换到C#,或者从Java到C++。

   

10. 能告诉我们接下来的几个月会有些什么样的进展吗?有没有什么特别有趣的?从远处看去你们的Haskell测试仪上将会进行什么尝试?

实际上我知道的也不比你多。微软准备将F#产品化,而我虽然为微软工作,我得到的微软的消息也大多来自媒体。至于我自己本人的工作,我所为之兴奋的,其中一个重要的东西就是并行,特别的是,事务性内存十分成功。你可以下载GHC,你就在你的多核机器上拥有了工作得很好的事务性内存系统,但这还不够,因为事务性内存意味着你可以繁衍线程。

这就像是fork进程,它们通过共享内存来通信,因此在你的控制之内。这是有所限制的并行,你可以利用到100个进程。很难找到这么多进程来做事,我想你需要这么多进程忙碌工作的地方应该是数据并行。数据并行-一个非常古老的点子-在一个大的集合,比如数组中,许多元素在同一时间做同样的事情。

将你的数组分成块,每个进程一块,每一进程负责它的块并在它们完成的时候达成一致。这就是数据并行,在实践中非常成功:MPI,Google的Map Reduce,高性能计算Fortran,这一类的东西。但这是平的,我的意思是一个数组的每一元素都做同样的事情,但你所完成的工作是顺序的。并不是每个算法都能够映射到这一范式。更加灵活的方式是:在数据并行中数组的每个元素都做同样的事情,但你所完成的工作本身应当是一个数据并行的算法。它跨越其它的向量以数据并行的方式工作,并且你每个元素所做的工作其本身也可能是数据并行的。

现在你可以看到,这一并行树在顶点有一些分支,以及一些小型的分支,然后变得像灌木一样复杂的结构,对于程序员来说好多了但对于实现来说却糟透了。这很难实现,因为在这一茂密的树上的叶子可能是对于一个浮点数的小型的浮点指令。你不会想要为此繁衍一个线程,你也不会想为这些树叶繁衍上百万的线程。你将会尝试找到一些方式来恢复扁平数据并行的粒度,就算数组有上百万元素,如果你有10个进程,每个处理器一千万元素:顺序循环也是十分有效的机制。

Guy Blelloch,90年代在卡耐基梅隆大学与他的同事Sabah一起展示了如何使用一个嵌套的数据并行算法,正如我之前给你描述的更加灵活的那种,来在编译时转换成一个新的算法,能够工作于一个扁平的数据数组。他们将这一嵌套的数据并行转换成扁平的数据并行,你知道如何有效的实现它。这非常棒因为你得到了一个更加灵活的编程机制,并且仍然有着高效的实现。Guy的工作在90年代中间转化了成了一种叫做Nastel语言,这是一个原型实现;其本质上是一个解释器。

它实际上对于很少的数据类型是非强类型的,而且它除了该嵌套数据并行几乎不做其它的事情。而我此刻之所以兴奋,是拿来Nastel的思想并通过将其应用于 Haskell而它们带入到21世纪。所以我与在澳大利亚悉尼的一些伙计一道将它加入了GHC-一个Haskell编译器。主导思想是有一个单一的语言能够支持分支类的表达型并行事务性内存以及这一嵌套数据并行的东西,还有半隐式的繁衍型并行,都包括在一个语言的框架里。

也许你在你的应用中不同的部分运用不同类型的并行,而你的应用某些部分让你觉得厌恶,因此你也会需要连接。我认为这一嵌套的数据并行思想是利用那些可爱的多核最有前途的方式。有趣的是,这一转换是一个非常重大的转换,它转换了所有的数据:数据表示改变了,代码改变了,所有的都改变了。这是对你程序里的源文本一种系统性的改变,而你能有机会这样的唯一可能就是因为程序是纯粹的。

如果它是命令式的话我们不会有机会。它就像是一个SQL查询优化器,对程序影响很大。而这种影响甚至可能让程序无法工作,如果还想要保持直接的副作用的话。如果我们真的能让嵌套数据并行工作起来,我想的话可能这就是杀手应用。这里有一定的风险,我不知道我能这样做,不过我们可以让它工作于弯曲的时钟,对于特定的有趣问题加速并且比Fortran还快,因为我们能编写的程序你无法用Fortran做到,而且可以工作于上百的处理器组上-这简直酷毙了!

   

11. 你什么时候能做到这些呢?

去年我们做成了一个原型。实际上,我们正处于寻找试验志愿者的阶段,所以今年下半段我们会寻找合适的试验志愿者,那些遇到困难的人们,这些困难又表现为有着很大的数据型的并行化潜力,对他们都带来了同样的困扰,但不是那种表面上很尴尬的并行因为你也许可以用MPI就轻易的解决了。所以我们在寻找善意的试验者,愿意来尝试一下也许一开始不一定工作得很好的新技术,但它如果发挥作用的话会非常的棒。

   

12. 最近我浏览了Meijer和其它人的文章。它谈到了编程,“香蕉”,“透镜”和“铁丝线”。我不知道你是否浏览了这篇文章,它似乎是对不同类型的同种递归的抽象,至少从我的理解来看。你认为这种类型的抽象,如果我们确实对递归进行抽象的话,是否会对优化和简化复杂的代码带来很多好处?

我正在思考如何来回答这个问题而不用假设大家都读过香蕉,透镜和铁丝网这一前提,这可是篇艰深的文章。这是Erik在加入微软之前写的,那时他还没有进入黑暗的一面。Erik像是从一个纯粹的抽象函数式程序员过渡到说:“不,这全是废话!我们需要用.NET的东西来替代它”,而现在又观念摇摆回来,实际上此刻又正好为.NET工作。某种程度上类型化的函数式编程语言能让人们很好的表达抽象。我所指的抽象是什么呢?一个过程,一个函数,一个方法是一种抽象因为在其自身内部用一种你希望相对简单的接口隐藏了实现的具体细节。

这些接口通常是类型化的。为了得到灵活的接口你需要十分丰富的类型,所以函数式编程语言就特别地成为了富类型系统,特别是多态-这是普遍的元素-所生长的场所。Erik所谈到的那一类结构能够很容易的转化成抽象的数学概念并映射到你能够实际执行的程序中。

我不认为在这样的前提下我能说得这么具体;相反,我宁愿说退回到实际的编程中,因为人们都有着相当的抽象念头,将它们映射到你可以执行的一些事物上然后说: “看啊!这就是一个编程模式”-这就回到了我们对模式的理解。你所看到的是一个编程模式;我们可以总结它并用到真正的程序中去,因此这是一个两步的过程。这就像monads所发生的一样:monads当中的抽象概念以一种你可以使用的东西被表达出来而现在我们可以用所有的方式来对其进行总结了。然而,类型化系统里的多态-类型化系统中的富多态-与纯粹的函数式天性的结合,直接将数学里面的观念翻译到了语言里,而你可以去设想,这看起来将会带来更多实际的结果。

{译注:这篇文章谈到了四种基本的递归操作符cata,ana,hylo,para,并以它们为基本来进行函数式演算,它们分别用四种不同的括号表示,'banana brackets': (|b, a|),'lense brackets' ([g, p]),'envelope brackets': [|(c, .), (g, p)|],'barbed-wire brackets': [],论文: Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire }

   

13. 回到F#,它吸取了很多函数式编程语言的概念,但仍是可变化的状态。你不认为小小的一点可变性会破坏整个系统吗,影响到整个编程语言的概念?

是的。.NET到F#是一种折衷。F#是.NET的高阶温床,而就F#的设计而言它是一门.NET语言。在一门.NET语言中你可以调用任意的.NET过程,而任意的.NET过程都可以做任何的事情。很难说这是一个纯粹的函数。相反F#引诱你进入一个纯粹的世界,通过提供一个丰富的良好设计的语言用以编写纯粹的函数式程序。实际上这回到了语法的问题,因为F#被翻译成MSIL-中间语言-与C#一样,所以你能用F#做的你都可以用C#做。你可以用C#来写那些函数式程序,但却要麻烦得多。

所以F#带来了这种便利;它使得编写函数的部分变得容易,命令式的部分也有这种可能,但你或许会正确的指出,这并不能得到保证。在有一些场合,比如并行的LinQ查询,必须要得以保证,而你能做的唯一办法就是通过约定。但也不至于这么差。我们对此已经习惯了-想想对可变数据的加锁保护。通常,锁和它所保护的数据之间的联系,大部分不是显式的存在于语言或者程序中,而是你的脑海中。

锁和数据的联系有时候是有一些私密的,但通常你访问什么数据之前需要获得什么样的锁在程序中是暗含的。这里并没有任何的保证,我们只是建立起约定并试图让它们工作。这并不像有一个岩石一般的保证那样让人满意。我们需要更强的保证,然而,我们也不能说:“因为你得不到保证,所以这是没用的”。这只是一个折衷,并且可能是一个很好的折衷。

   

14. 在LinQ当中我们传递任何东西都是用的引用,我们有闭包,以及引用对象内的闭包。这违背了使用lambda演算和列表内涵式的模型吗?

这是可以的。共享也没什么问题;引用也挺好的。如果你和我都指向同一个String,事实上这个String表示“Hello”,我们没必要保有两份 “Hello”,我们可以共享同一份拷贝,只要我们不改变它。共享并不是问题,它带来的影响才是。如果你共享的引用是指向不可变的值,那非常好。如果你共享的引用是指向可变的值而你更改了它们,那所有的事情就可能变糟。

   

15. 这些共享不是会对性能和优化带来好处吗?

当然,纯粹性有着一些良好的性能效果,因为你可以有力的共享更多的东西-我们在终端比较过2个NSD C++集合实现,当做一个并集时不得不拷贝输入集合,以防它们因此被更改,而F#的实现就不会。但它同样也会带来代价。假设我拥有两个String,并且想返回“Hello”和“World”给你,我想返回连接的String “Hello world”。我不能改变输入String的任中一个,因为它们是不可变的,所以我必须创建一个新的String-“Hello world”String-通过拷贝另外两个,或者至少拷贝其中一个并共享尾部。

总是会有一些拷贝参与进来,有些时候你会获得更好的共享性能,而有时候会变得差一些。我假设这最终会成为你的思维的一部分,你的思维重组的一部分。你会尝试区分你将会更改的数据和你不会去更改的数据,这样就不必严格的拷贝事物。你不能说这就是绝对的好或是绝对的坏,从性能的角度来看;它是变化的。

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT