51CTO编辑推荐:Scala编程语言专题
【51CTO独家特稿】上周我们从Scala创始人Martin Odersky的访谈录中了解了Scala创建的背景,这次让我们来看一看Martin Odersky对于Scala语言的设计目标是怎么说的。在创造“超越Java的语言”的过程中,具体都需要考虑到哪些方面呢?
让步
Frank Sommers:您之前提到,想要创造一种存在于Java体系内,集成Java基础架构的语言。为了做到这一点,Scala要做出什么样的让步,使其能够兼容Java平台?
Martin Odersky:很幸运,我们不需要做出太多妥协,或者说,很难判断我们所做出的所有妥协都是对我们不利或是有利的。其中一个我们不得不做的妥协是购买Java的静态重载模型。也许我们应该更积极地尝试一些其他方法,如使用多方法,尽管当时我们尝试过这一点,但并没有充分探讨有关多方法的设计。也许直到今天也还没有充分探讨过,所以我不能完全肯定这种方法是否行得通。这种方法本来有着令人振奋的可能性,但我们并没有采用它,因为我们想保持与Java的兼容性。
另外一件时而会引来人们否定的事是Scala既包含Traits技术又包含类技术。大家认为一个整洁的设计应该是只采用Traits技术。目前已有一些只采用Traits而放弃类概念的整洁设计,但我们并没有这样做。因为我们想从这两方面保留与Java的互用性。我们希望有一种方式可以让Java代码很容易地调用Scala代码,而Traits并不含有映射到Java的特性,因为在Java中根本不存在这种技术。因此,我们选择了Java中所拥有的类的概念,因为我们希望能够向后映射,这样我们就可以很容易地在两个方向保留互用性。
第3个问题,与其说是语言上的问题,不如说是类库的问题。我们很想要抛弃null(空值)这种概念。NULL是很多错误的根源。在Scala中,不允许null作为任何类型的可能值,取而代之的是选项类型(option type)。当然,有很多Java类库中的类都返回null,我们必须要解决这个问题。
51CTO编者注:在新语言兼容旧语言的这个方面,《Think in Java》和《Think in C++》的作者Bruce Eckel也曾经写过一篇文章,就C++和C的关系评论兼容老语言为新语言带来的好处和限制。这篇文章相关的讨论可以参见这里。
#p#
选择你需要的战斗
Bill Venners:为了能让Java程序员接受Scala,您都做了哪些努力?例如,像Java使用大括号而不是其它符号来分块程序,这使得C和C + +程序员感到使用Java就如同使用C和C++一样。
Martin Odersky:我们并没有为了推销产品而设计什么特殊功能,但是为了避免产生使用障碍,我们做了一些努力。举例来说,我认为大括号起了很好的分割作用,所以我选择使用它。我们本可以坚持使用begin/end或是其它符号,但我不认为这些符号能起到更好的效果。
其中有一个我们做了改动的地方。最初我们曾使用冒号-等号表示变量赋值,就像是Pascal、Modular和Ada那样,使用单个等号表示相等。很多编程理论会认为这是最恰当的方式。赋值并不表示相等,因此,你必须使用不同的符号。但后来我与一些使用Java的人进行交流。我得到的反应是,“哦,这看起来像是一个很有趣的语言。但是,为什么你要写冒号-等号?它是什么意思?”我解释说,意思就像Pascal中使用的那样。他们说,“那我明白了,但我不明白为什么你坚持要使用这个。”然后我意识到,这并不是我们想要坚持的东西。我们并不想说,“我们有了一个更好的语言,因为我们用冒号-等号代替了等号。”这完全是不值一提的小事,人们完全可以适应任何方式。因此,我们决定不再计较这些小事,而是有其他更重要的地方我们想要与众不同。
Bill Venners:这次您没有说那句您曾经说过的“选择你需要的战斗”。基本上,您认为,等号并不那么重要,还有其他您更在意的东西。那么那些重要的事情是什么?您有什么方法能够说服人们去改变他们的想法或程序?
Martin Odersky:我们首要关心的一件事是,拥有一个尽可能整洁的集成了函数式和面向对象的程序设计。我们希望拥有一流的功能,还希望拥有其他函数式程序设计的特征,如类型,泛型,模式匹配。我们希望能够以一个更加整洁的方式整合函数式和面向对象。这是我们从一开始就深感关心的事情。
后来,我们发现这实际上是很容易实现的,因为函数式语言有一套固定的特点。这些特点已经被深入研究和充分证明过了,因此,我们所面临的问题只是如何以最佳方式整合这些特点到面向对象程序设计。在做Pizza时,我们已经进行了尝试,在Scala,我认为我们得到了两者之间更顺畅的集成。但后来我们发现,在面向对象方面仍有很多事情有待开发。面向对象程序设计,至少摆在一个静态类型系统上,是一个非常未知领域。目前我们可以看到和使用一些已有的工作,但我们发现几乎所有的语言都做出了很大的妥协。
因此,随着开发Scala,我们开始发现如何能够混合对象,如何能够抽象自我类型,如何能够使用抽象类型成员,以及如何能够让这一切集成到一起。已有一些研究语言以特殊方法关注了以上几个方面,但几乎还没有任何主流语言,能够涵盖以上所有方面。最终结果表明,Scala的主要创新在于面向对象方面,这也是我们真正关心的事情。
#p#
面向对象的创新
51CTO推荐阅读:面向对象的思维过程
Bill Venners:您能不能给出一个具体的列表,列出一些您认为是Scala面向对象的创新?
Martin Odersky:首先,我们想要创造一个纯粹的面向对象的语言,其中的每个值都是一个对象,每个操作都是一个方法调用,每个变量都是一些类的成员。因此,我们不想要静态变量,但我们需要一些其他的东西来替代它们,所以我们提出了Singleton对象。但是,即使是Singleton对象仍然是全局结构。因此,我们所面临的挑战是如何尽可能少地使用它们,因为当你使用一个全局结构时,你就不能再改变它了。你不能实例化它。它很难测试。很难对它以任何方式进行修改。
因此,我们所面临的挑战是,如何能够不使用静态或全局思想来建立复杂的组件。尤其是,我们不得不处理组件间的递归依赖。例如我有两个组件A和B。A使用B,同时B使用A。我要如何才能让他们发现对方,并协同工作?我们所做的第一件事是基于mixin(混合式)合成的概念,然而,Java只有单一类和一串含有虚定义而没有代码的接口概念,Scala有类和Traits概念,其中Traits可以包含带有定义和函数域的方法,例如可以带有函数体。然后,我们就拥有了mixin合成,而不只是具有类实现接口,在mixin合成中,我们定义类和所有Traits。有关这项工作是如何工作的细节在此就不详述,我们称之为线性化。
因此,我们定义一个线性化Scala。但随之而来的问题是,怎么样能让一个mixin发现其他mixin?如果他们需要一些来自其他mixin的服务,他们如何指定目标?标准的面向对象方式是通过抽象成员,只要你处理方法,这种方式就会运作良好,但是,我们还必须要处理变量。如何才能让mixin找到对方的域?更重要的是,我们不得不处理类型。因为我们从一开始就有类型嵌套,就像内部类,这是另一个我认为非常重要的事情。如果你选择了mixin合成,一个Trait如何能够找到有关所有其他Trait的内部类,然后访问那些类?我们发现(不仅是发现,更是设计),我们可以通过在自我类型上定义一个抽象来做到这一点。
那么“自我类型上的抽象”究竟是什么意思?比如在一个类中,this类型代表什么意思?你会说,“表示这个类的类型。”大多数人都会这么回答。但实际上却没有任何令人信服的理由。This类型很可能是指其他的东西。只存在一个界定条件,就是当你实际创建一个类的实例的时候,然后这个你所创建的对象就与该类有同样的功能。这就与处理抽象方法时相同。你可以在一个类中定义一个抽象方法,并且不需要实现。这其实并不危险,因为当你创建一个该类的实例时,你将会检查是否所有抽象方法都有具体实现。因此,自我类型的抽象只是这种问题的一个一般化,也可以让你处理域和类型。
自我类型抽象的技术非常值得探索。早期,我们建立一种演算来研究这个问题,称为nu对象演算—νObj,我们于2003年,在面向对象程序设计欧洲会议上公布它( ECOOP 2003年)。我们最初发现这一概念只是作为一个技术诀窍,为了使演算过程能够简单。直到后来开始使用它之后我们发现,也许这将是有益于语言的东西。那时候我们还不太了解到底会有什么用处,但我们决定尝试。直到后来,我们才发现,它实现了我们想要的,即让一个Traits声明它所需要的来自其他Traits的东西。这就像是现在的一些工具,如Spring所实现的功能,被称为依赖注入。但通常这种依赖注入只能适用于域,也许对方法也管用,但我们可以让它还能应用于类型。此外,在Scala中,这种注入是静态完成而不是运行时完成,其次,它达到了对于内部类型的类型安全要求。因此,从某种意义上说,它比目前的一些工具如Spring做的更好。
我为什么要学习Scala?有什么好处吗?
51CTO推荐阅读:基于JVM的语言正在开始流行
Bill Venners:以后的采访中,我们将会继续再次探讨依赖注入,您刚才谈到关于自我类型的“抽象”。您提到的必须要解决的两件事是函数式和面向对象的融合以及面向对象的创新。如果我的工作室Java编程,在实际工作中,这些东西如何能够帮助我?我能得到什么实质的益处?
Martin Odersky:我们正面临的挑战之一,是我们想融合函数式和面向对象。我们很早就确定了这种概念,即不可变类是非常非常重要的。现在大家都在谈论不可变类,因为人们认为这是解决目前由多核电脑所引起的问题的一个关键方法。大家都说,不管你做什么,你都要尝试让你的代码尽可能多的使用不可变类。在Scala中,我们也从很早就采用这种方法。五,六年前,我们开始深刻考虑不可变的类。但事实证明,大量的面向对象领域一直都在使用可变对象。对于他们来说,可变状态和对象是同一回事:可变状态是一个对象中必不可少的组成部分。我们必须从本质上解释这个问题。
例如,对于一个标准的Java对象,你将创建一个域,基本上是一个可变域。然后你还需考虑构造函数,接收参数并为域赋值。这就是可变的概念,内置到每一个Java类。现在,Java具有final域概念,它不被认为是可变的,因为它只在构造函数中被赋值一次。但你仍然能看到赋值操作。我们希望能有一个更清晰的概念,让你不需要看到构造函数和赋值操作。
使用Scala我们最后所做的事是直接确定类的参数。你只需要在类名后写一个参数列表,这些就成为类的参数。不存在独立可变域和构造函数的概念,这些实际上转变成了一些我们必须要解决的问题。其中之一是,如果你想要有几个构造函数,那要怎么办?我们必须为其确定语法和规则。你可以在你的primary(主要)构造函数之外使用auxiliary(辅助)构造函数。另一个问题是,如果你希望你的参数作为一个域可见化,该怎么办?需要创建一个单独的域,然后再赋值吗?或者是有可能传递参数给一个类,然后立即变成一个域而对其他人可用?因此,我们必须为其创建语法,我相信这种做法也是全新的。最后在面向对象方面还有很多其他新颖的想法。
Bill Venners:对于我这样的一个Java程序员,能得到什么益处?
Martin Odersky:好处就是你可以用更简洁的语法来定义类。使用Scala编写类,能够更容易、更简洁。如果你想要使用不可变类,那将更加容易,因为它非常适合做这件事。你还可以像使用Java那样来使用Scala定义可变类。这甚至比用Java更方便,但Scala真正耀眼的地方还在于它的不可变类。它比Java更自然、更简明。
编者后记:
随着业界开始流传基于JVM的语言正在开始流行这样的声音,处在基于JVM的非Java语言之首的Groovy和Scala被寄以相当大的期望。曾经有一篇英文文章对于几个JVM语言(Groovy,Scala和JRuby)进行了比对,结果是无论从速度,缓存需求还是垃圾处理方面,Scala都是最适合企业级开发的语言。究竟Scala是否真的如此神奇,还让我们拭目以待。
【相关阅读】