【51CTO精選譯文】幾個月前鉴逞,我開始使用 Scala。我用過的編程語言還有 Pascal司训、C构捡、C++、Java壳猜、PHP勾徽、Ruby 和 Groovy,但是统扳,與所有那些我用過的語言相比喘帚,我發(fā)覺 Scala 是一門與眾不同的語言。我是在看到 Twitter 上關(guān)于 Ruby 和 Scala 討論之后咒钟,才開始我的 Scala 編程之旅的〈涤桑現(xiàn)在,使用 Scala 編程已經(jīng)幾個月了朱嘴,關(guān)于 Scala 我有兩點想法倾鲫,雖然類似的想法已廣為人知,但我仍很想與你們分享:
51CTO編輯推薦:
Scala編程語言專題
◆Scala 的確很棒萍嬉。
◆我的確認(rèn)為計算機(jī)學(xué)院應(yīng)該開一門 Scala 的語言課程乌昔。
在這篇文章中,我會講述為什么我會有這樣的想法帚湘,在此之前玫荣,有幾點我想要先聲明一下:
本文無意對編程語言進(jìn)行評比,我要講述的主體是為什么你應(yīng)該學(xué)習(xí) Scala大诸。
51CTO之前曾發(fā)布過一篇 Java 程序員為何要學(xué)習(xí)Scala的文章捅厂,可能也會對你有所幫助。
目前 Scala 有兩個實現(xiàn)方式资柔,一種是在 JVM(Java 虛擬機(jī))上運行焙贷,另一種是在 CLR(Common Language Runtime 的縮寫,即公共語言運行庫)上運行贿堰。不過辙芍,JVM 的實現(xiàn)方式更為成熟。如果你想要使用 .Net framework 框架,我認(rèn)為最好還是聽從 Lift framework 框架創(chuàng)始人大衛(wèi)·波拉克(David Pollack)的建議:使用 F#故硅。但在這篇文章中庶灿,我將只關(guān)注 JVM 這種實現(xiàn)方式。
我是一個 Ruby 程序員吃衅,并且我會繼續(xù)喜歡 Ruby往踢,因為它是我見到過的最棒的動態(tài)語言。但我也喜歡 Scala徘层,因為在其他工作領(lǐng)域峻呕,它提供的某些功能非常強(qiáng)大。
現(xiàn)在趣效,讓我們來仔細(xì)分析一下瘦癌,是哪些原因讓我選擇 Scala 作為我的下一個編程語言:
強(qiáng)大的編程語言
Scala 是一門非常強(qiáng)大的語言,它允許用戶使用命令和函數(shù)范式進(jìn)行編寫代碼跷敬,因此讯私,編程時你可以使用常用的命令式語句,就像我們使用 C干花、Java妄帘、PHP 以及很多其他語言一樣,而且池凄,你也可以使用類似 Lisp 語言中函數(shù)式語句,還有鬼廓,你可以混合使用這兩種風(fēng)格的語句肿仑,就像 Ruby 或 Groovy。
不過碎税,當(dāng)我們談?wù)摰暮瘮?shù)范式時尤慰,與 Ruby 和 Groovy 有一點不同的地方,那就是 Scala 幾乎支持函數(shù)語言中所有已知的功能雷蹂,比如伟端,模式匹配(Pattern matching)、延遲初始化(Lazy initialization)匪煌、偏函數(shù)(Partial Function)责蝠、不變性(Immutability),等等...即是說萎庭,認(rèn)識到這樣一個事實是非常重要的:Scala 的強(qiáng)大源自它對函數(shù)范式的支持霜医,而后者令 Scala 成為一種高等級(high-level)的編程語言。對于高等級的編程語言驳规,你只需關(guān)注 what(做什么)而不是如何做(how)肴敛。
下面,讓我們看一個 Java 示例:
int[] x = {1,2,3,4,5,6};
ArrayList res = new ArrayList();
for (int v : x) {
if (v % 2 == 1) res.add(new Integer(v));
}
仔細(xì)看一下上面這段示例代碼,你會注意到医男,我想要做的“what”部分(過濾掉奇數(shù)值)僅出現(xiàn)在第四行中砸狞,而其余行則是“how”如何做的部分(結(jié)果變量的初始化以及一個循環(huán)操作)。如果我想要再寫一個過濾器镀梭,用于篩選偶數(shù)值趾代,那就需要再寫五行代碼,而使用一門像 Scala 這樣的高等級語言丰辣,你只需編寫“what”那部分的代碼:
val x = Array(1,2,3,4,5,6)
val res = x filter ( _ % 2 == 1 ) //過濾奇數(shù)值
val res2 = x filter ( _ % 2 == 0 ) //過濾偶數(shù)值
我們可以看到撒强,相對于上文中的 Java 代碼段,這段代碼更加簡潔笙什,而且具有更好的可讀性飘哨。
高效
Scala 是一種高效的編程語言,實際上琐凭,根據(jù)最新的 benchmark 性能測試芽隆,它幾乎和 Java 一樣快捷。在 JVM 上實現(xiàn)的 Scala 代碼统屈,可以編譯為字節(jié)碼胚吁,在這一過程中,代碼通過優(yōu)化階段進(jìn)行編譯愁憔。尾遞歸優(yōu)化是一個很好的示例腕扶,它可幫助用戶專注于函數(shù)范式而無需以犧牲性能為代價。還有一個示例是吨掌,將 Scala 值類型對象轉(zhuǎn)換為 Java 基本類型時進(jìn)行的優(yōu)化半抱。
可擴(kuò)展
Scala 語言本身的名字 Scala 來自 Scalable(可擴(kuò)展的)一詞,這意味著這種語言可以按照用戶的需求進(jìn)行擴(kuò)展膜宋。因此窿侈,從根本上來講,用戶可以添加新的類型和控制結(jié)構(gòu)秋茫。比如史简,我想要添加一個簡單的“l(fā)oop”控制結(jié)構(gòu):
// 一個簡單的構(gòu)建
def loop(range: Range)(op: Int=> Unit) {
range foreach (op)
}
loop(1 to 5 ){println} // 1 2 3 4 5
loop(1 to 5 ){x => if(x % 2 == 0) println(x)} // 2 4
還有幾個更為復(fù)雜的例子,Actor lib肛著,它是作為擴(kuò)展被添加到 Scala 這一語言中的圆兵,我們將在下文中對它展開討論。
不過策泣,Scala 之所以是可擴(kuò)展的衙傀,在于互相關(guān)聯(lián)的兩點:它是真正的面向?qū)ο蟮恼Z言和真正的函數(shù)式語言。
面向?qū)ο?/strong>
Scala 中每個事物都是對象(對象的方法除外)萨咕,因此统抬,沒有必要對基本(primitive)類型或引用類型進(jìn)行區(qū)分,這就是所謂的:統(tǒng)一對象模型(Uniform Object Model)。但是聪建,正如我之前在優(yōu)化流程中所提到的钙畔,值類型對象被轉(zhuǎn)換為 Java 基本類型,因此不必?fù)?dān)心性能的問題金麸。其內(nèi)部還包含為類方法分組的單件對象(Singleton object)擎析。
◆所有操作都是方法調(diào)用,+ - * ! / 都是方法挥下,因此揍魂,沒有必要進(jìn)行操作符重載。
◆非常精細(xì)的訪問控制棚瘟,用戶可以控制對某些包的某些方法的訪問现斋。
◆Scala 具有 trait,與 Ruby 中的 mixin 類似偎蘸,就像 Java 中的 interfaces庄蹋,但實現(xiàn)了某些它們的方法,因此迷雪,用戶在箱體(box)之外擁有富封裝器(wrapper)和富交互接口(interface)限书。
函數(shù)式語言
函數(shù)式語言具有很多特點,不過在擴(kuò)展性這一語境中章咧,我們所關(guān)心的是兩個事實:
◆函數(shù)是第一等級(first-class)的值
這表示用戶可以將函數(shù)作為值傳遞倦西,也可以作為值返回。這樣可以獲得簡潔而具有可讀性的代碼慧邮,正如上文中作為示例的過濾代碼段调限。
◆純函數(shù)(pure function)
Scala 支持沒有副作用的純函數(shù),這意味著:如果你的輸入相同误澳,那么輸出結(jié)果也總是相同。這樣能夠讓代碼更為安全秦躯,對代碼測試也更為方便忆谓。
但是,Scala 是通過什么方式來支持純函數(shù)的呢踱承?通過不變性(immutability):偏向固定的引用(與 java 中的 final 或其他語言中的 constant 類似)以及具有不變的數(shù)據(jù)結(jié)構(gòu)倡缠,一旦創(chuàng)建便不可修改。
不變性是擁有純函數(shù)的安全保證茎活,但并不是唯一的方式昙沦。沒有不變性,你仍然可以編寫安全的代碼载荔。這就是為什么 Scala 不是強(qiáng)制推行不變性而只是鼓勵使用它盾饮。最終,你會發(fā)現(xiàn) Scala 中許多數(shù)據(jù)結(jié)構(gòu)具有了兩種實現(xiàn)方式,一種是可變的丘损,另一種是不可變的普办,不可變的數(shù)據(jù)結(jié)構(gòu)是缺省導(dǎo)入的。
每當(dāng)提到不變性時徘钥,有人就會開始擔(dān)心性能的問題衔蹲,對于某些情況,這種擔(dān)憂并非毫無來由呈础,但對于 Scala舆驶,最終結(jié)果卻與這一擔(dān)憂相反。不可變的數(shù)據(jù)結(jié)構(gòu)相對于可變的數(shù)據(jù)結(jié)構(gòu)而钞,更有助于獲得較高的效率沙廉。其原因之一在于強(qiáng)大的垃圾收集器(garbage collector),與 JVM 中的垃圾收集器類似笨忌。
更佳的并行模型
當(dāng)涉及到線程這一問題時蓝仲,Scala 支持傳統(tǒng)的 shared data 模型。但是官疲,使用這種模型較長一段時間之后袱结,許多人發(fā)現(xiàn)使用這種模型編寫代碼,非常難以實現(xiàn)以及進(jìn)行測試途凫。你總是需要考慮死鎖問題和競爭條件垢夹。因此,Scala 提供了另一個稱為 Actor 的并行模型维费,其中果元,actor 通過它的收件箱來發(fā)送和接收非同步信息,而不是共享數(shù)據(jù)犀盟。這種方式被稱為:shared nothing 模型而晒。一旦你不再顧慮共享數(shù)據(jù)的問題,也就不必再為代碼同步和死鎖問題而頭痛阅畴。
被發(fā)送信息的不變性本質(zhì)以及 actor 中串行處理倡怎,這兩者使得對于并行的支持更為簡便。
有關(guān) Scala 并行的問題贱枣,請參閱這篇文章监署,對于這個概念你會有更好的理解。
在講述下一個要點之前纽哥,我需要提到這樣一個事實钠乏,一些人將 Actor 的使用視為編程語言的一種進(jìn)化。正如春塌,Java 的出現(xiàn)晓避,將程序員們從指針和內(nèi)存管理的泥淖中拯救出來一樣簇捍,Scala 的到來,讓程序員們不必再為代碼同步以及共享數(shù)據(jù)模型整天苦思冥想够滑。
靜態(tài)類型
當(dāng)我想要講述這一要點的時候垦写,才發(fā)現(xiàn),對于靜態(tài)類型語言的正反兩面彰触,我試圖給予同樣的關(guān)注梯投。事實上,關(guān)于這一話題的爭論總是沒完沒了况毅,但我要作出兩點總結(jié)分蓖,而這兩點是大多數(shù)人討論的熱點:
◆使用靜態(tài)類型語言編寫的代碼更加健壯(robust)
TDD 的存在,讓許多關(guān)于動態(tài)類型語言和健壯代碼的討論失去了意義尔许,雖然這是正確的,當(dāng)我們?nèi)匀徊荒芎鲆曔@樣一個事實:對于動態(tài)類型語言味廊,你需要編寫更多的測試代碼來檢查類型蒸甜,而在靜態(tài)類型語言中,你可以將這些問題交給編譯器處理余佛。此外柠新,還有一些人認(rèn)為,使用靜態(tài)類型語言辉巡,你的代碼將具有更好的自我記錄恨憎。
◆使用靜態(tài)類型語言編寫的代碼過于嚴(yán)格和冗長
像我這樣的動態(tài)類型語言的粉絲,認(rèn)為通過鴨子類型(duck typing)可以寫出更具動態(tài)性的代碼結(jié)構(gòu)郊楣。但同時他們還會抱怨憔恳,靜態(tài)類型語言導(dǎo)致代碼冗長。
關(guān)于靜態(tài)類型與動態(tài)類型的爭論净蚤,在51CTO之前發(fā)布的這篇文章中可以看到更多信息钥组。
作為靜態(tài)類型語言,Scala 具有第一條中提到的優(yōu)點今瀑,但是者铜,第二點呢?
Scala 具有一個靈活的類型系統(tǒng)放椰,并且可能是這一類型中最好的。很多情況下愉粤,如果你沒有指定類型砾医,這一系統(tǒng)將能夠?qū)︻愋瓦M(jìn)行推斷。
例如衣厘,你可以這樣編寫代碼:
val list: List[String] = List("one", "two", "three")
//list: List[String] = List(one, two, three)
val s: String = "Hello World!"
//s: java.lang.String = hello world!
```
但你也可以這樣編寫代碼:
```
val list = List("one", "two", "three")
//list: List[String] = List(one, two, three)
val s = "Hello World!"
//s: java.lang.String = hello world!
```
非常好如蚜,無論如何压恒,它解決了代碼冗長的問題。但像鴨子類型(duck typing)那樣的問題错邦,會怎樣呢探赫?
答案還是:Scala 的類型系統(tǒng)具有的某些靈活性,可以讓你編寫如下的代碼:
```
def eat[T <: Animal](a: T) // whatever
```
其中撬呢,我們將類型 T 定義為 Animal 的子類型伦吠。還可以更加靈活:
```
def eat[T <: {def eat(): Unit}](a: T) // whatever
```
其中,我們將類型 T 定義為一個具有非法 eat 的類型魂拦。
事實上毛仪,Scala 的類型系統(tǒng)非常豐富,你可以在這里找到更多信息芯勘。
**模式匹配**
我必須坦白箱靴,在猶豫良久之后,我才決定寫一寫 Scala 的這一特點荷愕。事實上衡怀,我本來沒有打算討論 Scala 的函數(shù)功能,但看到一篇有關(guān)對象分支(switch)應(yīng)用的文章后安疗,我想抛杨,還是有必要聊聊這個特點。以下內(nèi)容基本上都來自這篇博客文章:
模式匹配究竟是用來做什么的茂契?它讓你可以將一個值對多種情況(case)進(jìn)行匹配蝶桶,有點類似 Java 中的分支(switch)語句。但它不是僅僅匹配數(shù)字(這是分支語句的作用)掉冶,而是用戶能夠?qū)Ρ举|(zhì)上為對象創(chuàng)建形式(creation form)的事物進(jìn)行匹配真竖。
以下示例也來自上文提到的博客:
```
x match {
case Address(Name(first, last), street, city, state, zip) => println(last + ", " + zip)
case _ => println("not an address") // 缺省情況
}
```
對于第一種情況,模式 Name(first, last) 嵌套在模式 Address(…) 中厌小。 其中的 last 值恢共,被傳遞到 Name 構(gòu)造函數(shù),然后進(jìn)行提取璧亚,因此在箭頭右側(cè)的表達(dá)式中是可用的讨韭。
***模式匹配的意義***
*為什么你需要模式匹配?我們都會有復(fù)雜的數(shù)據(jù)癣蟋。如果我們堅持嚴(yán)格的面向?qū)ο缶幊掏赶酰敲次覀兙筒辉溉リP(guān)注數(shù)據(jù)樹的內(nèi)部情況。相反疯搅,我們想要調(diào)用方法濒生,讓方法來做這些事情。如果我們有能力完成這件事幔欧,就不會非常需要模式匹配罪治,因為方法滿足了我們的要求丽声。但是,很多時候?qū)ο鬀]有我們所需的方法觉义,并且我們不能(或不愿)為對象添加新的方法雁社。*
因此,模式匹配被認(rèn)為是一種獲得擴(kuò)張性的有效方法晒骇,并且霉撵,它還為該問題提供了一種不錯的解決方案,訪問者設(shè)計模式所導(dǎo)致的冗長除外厉碟。
不管怎樣喊巍,強(qiáng)烈推薦你看看上面所提到的文章中”擴(kuò)展性的兩個方向“(Two directions of extensibility)那個小節(jié)。
**簡單的 DSL(特定領(lǐng)域語言)**
編寫 DSL箍鼓,Scala 是一個很好的選擇崭参。事實上,Scala 適用于內(nèi)部和外部 DSL款咖。在這篇文章中何暮,你可以找到一些使用 Ruby 和 Scala 編寫內(nèi)部 DSL 的特點比較。下面這篇文章也很棒铐殃,是關(guān)于使用 Scala 編寫內(nèi)部 DSL 的:Boolean Algebra Internal DSL in Scala (aka fun with Unicode names )海洼。
此外,對于外部 DSL富腊,Scala 也應(yīng)該是首選語言坏逢,背后的原因是解析器組合子庫(parser combinator lib),它讓為新語言編寫編譯器成為一件很酷的事赘被。
**與 Java 代碼之間的互操作性**
在 JVM 上的實現(xiàn) Scala 的程序可以無縫地與 Java 平臺整合是整,很多 Scala 類型實際上都編譯為 Java 類型,因此民假,用戶可以放心地使用 Java 類型浮入。而且,你也可以混合地使用 JVM 語言來編程羊异,如:JRuby事秀、Groovy、Clojure 等野舶。這里有一篇不錯的文章易迹,提供了這種示例。
**學(xué)習(xí)型語言**
我有兩個習(xí)慣平道,在 Scala 的學(xué)習(xí)過程中赴蝇,我堅持了這兩個習(xí)慣:
◆遇到新的技術(shù)術(shù)語,訪問維基百科巢掺,理解更多信息句伶;比如 Function literal(文本函數(shù))、Referentially transparent(引用透明度)陆淀、Partial function(偏函數(shù))考余、Currying(科里華),還有很多其他術(shù)語轧苫。
◆參考我對其他語言的理解楚堤,檢查這些術(shù)語的涵義是否實現(xiàn)。
通過一些好的練習(xí)含懊,如編寫沒有副作用的純函數(shù)身冬,將精力集中在代碼中的“what”部分,而將“how”的部分交給語言處理岔乔;這兩個習(xí)慣讓我獲得更多知識酥筝,也提高了代碼的質(zhì)量。
**團(tuán)隊**
Scala 由馬丁·奧德斯基(Martin Odersky)設(shè)計雏门,他是瑞士聯(lián)邦理工學(xué)院洛桑分校(EPFL)編程方法實驗室小組的管理者嘿歌。奧德斯基曾受雇于 Sun 公司編寫 Java 1.1 編譯器,他還是 Java 1.1 到 Java 1.4 的 Javac 主要開發(fā)者茁影。此外宙帝,他還是 Java Generics 的提出者。51CTO編輯曾通過電子郵件與奧德斯基就 Scala 的語言特性進(jìn)行了交流募闲,并得到了回復(fù)信件[如下](http://developer.51cto.com/art/200906/128037.htm)步脓。
Scala 現(xiàn)在由奧德斯基和他在瑞士聯(lián)邦理工學(xué)院洛桑分校的團(tuán)隊維護(hù)。不過浩螺,還有其他一些具有才華的開發(fā)者參與靴患,通過多年的工作,他們共同打造出了 Scala 這一編程語言年扩。
**來源**
Scala 受到了[多種語言的啟發(fā)](http://developer.51cto.com/art/200907/134890.htm):
◆大多數(shù)的句法來自 Java 和 C#蚁廓。
◆其他一些元素也來自 Java,比如:基本類型(basic type)厨幻、類庫相嵌,以及其運行模型。
◆它所用的統(tǒng)一對象模型是由 Smalltalk 最先使用的况脆。
◆通用嵌套(universal nesting)的理念也出現(xiàn)在 Algol饭宾、Simula 中,而且最近還出現(xiàn)在 Beta 和 gbeta 中格了。
◆函數(shù)式編碼的方法在精神上也與 ML 語言家族類似看铆,該語言家族中包含 SML、OCaml盛末,以及最主要的成員 [F#](http://developer.51cto.com/art/200906/130786.htm)弹惦。
◆Scala 標(biāo)準(zhǔn)庫中的許多較高階的函數(shù)否淤,也出現(xiàn)在 ML 和 Haskell 中。
◆Scala 的隱式參數(shù)也是受到 Haskell 類型類的啟發(fā)棠隐。Scala 基于 actor 的并行庫主要是受到 Erlang 的啟發(fā)石抡。
◆將插入(infix)操作符視為函數(shù),以及允許文本函數(shù)(或 block 區(qū)塊)作為參數(shù)助泽,以使庫能夠定義控制結(jié)構(gòu)啰扛,這些特定的理念可以回溯至 Iswim 和Smalltalk。
**專家觀點**
實際上嗡贺,像詹姆士·斯特拉坎(James Strachan:編程語言 Groovy 的創(chuàng)始人)的[這樣的言語](http://developer.51cto.com/art/200907/134785.htm)讓人感到有點驚喜:
說實話隐解,如果有人在 2003 年給我一本由馬丁·奧德斯基(Martin Odersky)、萊克斯·斯彭(Lex Spoon)和比爾·文納斯(Bill Venners)合著的《Programming in Scala》诫睬,我很可能不會再去創(chuàng)建 Groovy煞茫。
在結(jié)束之前,我做一下總結(jié):
我喜歡 Scala岩臣,因為它是高效的溜嗜、學(xué)習(xí)型的語言,具有較好的并行模型架谎,以及非常適用于編寫 DSL炸宵。
原文:Scala is my next choice
作者:khelll 譯者:[司馬牽牛](http://tsaizb.blog.51cto.com/)
【相關(guān)閱讀】
[Scala取代Java?可能嗎谷扣?熱議仍持續(xù)不斷
](http://developer.51cto.com/art/200907/135770.htm)
[Groovy創(chuàng)始人:Java面臨終結(jié) Scala將取而代之
](http://developer.51cto.com/art/200907/134785.htm)
[Scala如何改變了我的編程風(fēng)格:從命令式到函數(shù)式
](http://developer.51cto.com/art/200906/127825.htm)
[Scala的類型系統(tǒng) 比Java更靈活
](http://developer.51cto.com/art/200906/126710.htm)
[Java程序員土全,你為什么要關(guān)注Scala
](http://developer.51cto.com/art/200905/125526.htm)