Ruby高手點評Scala編程語言十大絕招

原文地址

【51CTO精選譯文】幾個月前,我開始使用 Scala。我用過的編程語言還有 Pascal、C、C++、Java、PHP、Ruby 和 Groovy,但是,與所有那些我用過的語言相比,我發覺 Scala 是一門與眾不同的語言。我是在看到 Twitter 上關于 Ruby 和 Scala 討論之后,才開始我的 Scala 編程之旅的?,F在,使用 Scala 編程已經幾個月了,關于 Scala 我有兩點想法,雖然類似的想法已廣為人知,但我仍很想與你們分享:

51CTO編輯推薦:
Scala編程語言專題

◆Scala 的確很棒。
◆我的確認為計算機學院應該開一門 Scala 的語言課程。
在這篇文章中,我會講述為什么我會有這樣的想法,在此之前,有幾點我想要先聲明一下:
本文無意對編程語言進行評比,我要講述的主體是為什么你應該學習 Scala。
51CTO之前曾發布過一篇 Java 程序員為何要學習Scala的文章,可能也會對你有所幫助。
目前 Scala 有兩個實現方式,一種是在 JVM(Java 虛擬機)上運行,另一種是在 CLR(Common Language Runtime 的縮寫,即公共語言運行庫)上運行。不過,JVM 的實現方式更為成熟。如果你想要使用 .Net framework 框架,我認為最好還是聽從 Lift framework 框架創始人大衛·波拉克(David Pollack)的建議:使用 F#。但在這篇文章中,我將只關注 JVM 這種實現方式。
我是一個 Ruby 程序員,并且我會繼續喜歡 Ruby,因為它是我見到過的最棒的動態語言。但我也喜歡 Scala,因為在其他工作領域,它提供的某些功能非常強大。
現在,讓我們來仔細分析一下,是哪些原因讓我選擇 Scala 作為我的下一個編程語言:

強大的編程語言
Scala 是一門非常強大的語言,它允許用戶使用命令和函數范式進行編寫代碼,因此,編程時你可以使用常用的命令式語句,就像我們使用 C、Java、PHP 以及很多其他語言一樣,而且,你也可以使用類似 Lisp 語言中函數式語句,還有,你可以混合使用這兩種風格的語句,就像 Ruby 或 Groovy。

不過,當我們談論的函數范式時,與 Ruby 和 Groovy 有一點不同的地方,那就是 Scala 幾乎支持函數語言中所有已知的功能,比如,模式匹配(Pattern matching)、延遲初始化(Lazy initialization)、偏函數(Partial Function)、不變性(Immutability),等等...即是說,認識到這樣一個事實是非常重要的:Scala 的強大源自它對函數范式的支持,而后者令 Scala 成為一種高等級(high-level)的編程語言。對于高等級的編程語言,你只需關注 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));   
    } 

仔細看一下上面這段示例代碼,你會注意到,我想要做的“what”部分(過濾掉奇數值)僅出現在第四行中,而其余行則是“how”如何做的部分(結果變量的初始化以及一個循環操作)。如果我想要再寫一個過濾器,用于篩選偶數值,那就需要再寫五行代碼,而使用一門像 Scala 這樣的高等級語言,你只需編寫“what”那部分的代碼:

    val x = Array(1,2,3,4,5,6)   
    val res = x filter ( _ % 2 == 1 ) //過濾奇數值
    val res2 = x filter ( _ % 2 == 0 ) //過濾偶數值

我們可以看到,相對于上文中的 Java 代碼段,這段代碼更加簡潔,而且具有更好的可讀性。

高效
Scala 是一種高效的編程語言,實際上,根據最新的 benchmark 性能測試,它幾乎和 Java 一樣快捷。在 JVM 上實現的 Scala 代碼,可以編譯為字節碼,在這一過程中,代碼通過優化階段進行編譯。尾遞歸優化是一個很好的示例,它可幫助用戶專注于函數范式而無需以犧牲性能為代價。還有一個示例是,將 Scala 值類型對象轉換為 Java 基本類型時進行的優化。

可擴展
Scala 語言本身的名字 Scala 來自 Scalable(可擴展的)一詞,這意味著這種語言可以按照用戶的需求進行擴展。因此,從根本上來講,用戶可以添加新的類型和控制結構。比如,我想要添加一個簡單的“loop”控制結構:

    // 一個簡單的構建
    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

還有幾個更為復雜的例子,Actor lib,它是作為擴展被添加到 Scala 這一語言中的,我們將在下文中對它展開討論。
不過,Scala 之所以是可擴展的,在于互相關聯的兩點:它是真正的面向對象的語言和真正的函數式語言。

面向對象
Scala 中每個事物都是對象(對象的方法除外),因此,沒有必要對基本(primitive)類型或引用類型進行區分,這就是所謂的:統一對象模型(Uniform Object Model)。但是,正如我之前在優化流程中所提到的,值類型對象被轉換為 Java 基本類型,因此不必擔心性能的問題。其內部還包含為類方法分組的單件對象(Singleton object)。
◆所有操作都是方法調用,+ - * ! / 都是方法,因此,沒有必要進行操作符重載。
◆非常精細的訪問控制,用戶可以控制對某些包的某些方法的訪問。
◆Scala 具有 trait,與 Ruby 中的 mixin 類似,就像 Java 中的 interfaces,但實現了某些它們的方法,因此,用戶在箱體(box)之外擁有富封裝器(wrapper)和富交互接口(interface)。

函數式語言
函數式語言具有很多特點,不過在擴展性這一語境中,我們所關心的是兩個事實:
◆函數是第一等級(first-class)的值
這表示用戶可以將函數作為值傳遞,也可以作為值返回。這樣可以獲得簡潔而具有可讀性的代碼,正如上文中作為示例的過濾代碼段。

◆純函數(pure function)
Scala 支持沒有副作用的純函數,這意味著:如果你的輸入相同,那么輸出結果也總是相同。這樣能夠讓代碼更為安全,對代碼測試也更為方便。
但是,Scala 是通過什么方式來支持純函數的呢?通過不變性(immutability):偏向固定的引用(與 java 中的 final 或其他語言中的 constant 類似)以及具有不變的數據結構,一旦創建便不可修改。
不變性是擁有純函數的安全保證,但并不是唯一的方式。沒有不變性,你仍然可以編寫安全的代碼。這就是為什么 Scala 不是強制推行不變性而只是鼓勵使用它。最終,你會發現 Scala 中許多數據結構具有了兩種實現方式,一種是可變的,另一種是不可變的,不可變的數據結構是缺省導入的。
每當提到不變性時,有人就會開始擔心性能的問題,對于某些情況,這種擔憂并非毫無來由,但對于 Scala,最終結果卻與這一擔憂相反。不可變的數據結構相對于可變的數據結構,更有助于獲得較高的效率。其原因之一在于強大的垃圾收集器(garbage collector),與 JVM 中的垃圾收集器類似。

更佳的并行模型
當涉及到線程這一問題時,Scala 支持傳統的 shared data 模型。但是,使用這種模型較長一段時間之后,許多人發現使用這種模型編寫代碼,非常難以實現以及進行測試。你總是需要考慮死鎖問題和競爭條件。因此,Scala 提供了另一個稱為 Actor 的并行模型,其中,actor 通過它的收件箱來發送和接收非同步信息,而不是共享數據。這種方式被稱為:shared nothing 模型。一旦你不再顧慮共享數據的問題,也就不必再為代碼同步和死鎖問題而頭痛。
被發送信息的不變性本質以及 actor 中串行處理,這兩者使得對于并行的支持更為簡便。
有關 Scala 并行的問題,請參閱這篇文章,對于這個概念你會有更好的理解。
在講述下一個要點之前,我需要提到這樣一個事實,一些人將 Actor 的使用視為編程語言的一種進化。正如,Java 的出現,將程序員們從指針和內存管理的泥淖中拯救出來一樣,Scala 的到來,讓程序員們不必再為代碼同步以及共享數據模型整天苦思冥想。

靜態類型
當我想要講述這一要點的時候,才發現,對于靜態類型語言的正反兩面,我試圖給予同樣的關注。事實上,關于這一話題的爭論總是沒完沒了,但我要作出兩點總結,而這兩點是大多數人討論的熱點:
◆使用靜態類型語言編寫的代碼更加健壯(robust)
TDD 的存在,讓許多關于動態類型語言和健壯代碼的討論失去了意義,雖然這是正確的,當我們仍然不能忽視這樣一個事實:對于動態類型語言,你需要編寫更多的測試代碼來檢查類型,而在靜態類型語言中,你可以將這些問題交給編譯器處理。此外,還有一些人認為,使用靜態類型語言,你的代碼將具有更好的自我記錄。
◆使用靜態類型語言編寫的代碼過于嚴格和冗長
像我這樣的動態類型語言的粉絲,認為通過鴨子類型(duck typing)可以寫出更具動態性的代碼結構。但同時他們還會抱怨,靜態類型語言導致代碼冗長。
關于靜態類型與動態類型的爭論,在51CTO之前發布的這篇文章中可以看到更多信息。
作為靜態類型語言,Scala 具有第一條中提到的優點,但是,第二點呢?
Scala 具有一個靈活的類型系統,并且可能是這一類型中最好的。很多情況下,如果你沒有指定類型,這一系統將能夠對類型進行推斷。
例如,你可以這樣編寫代碼:

    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 的類型系統具有的某些靈活性,可以讓你編寫如下的代碼:
```
    def eat[T <: Animal](a: T) // whatever
```

其中,我們將類型 T 定義為 Animal 的子類型。還可以更加靈活:
```
   def eat[T <: {def eat(): Unit}](a: T) // whatever
```

其中,我們將類型 T 定義為一個具有非法  eat 的類型。
事實上,Scala 的類型系統非常豐富,你可以在這里找到更多信息。

**模式匹配**
我必須坦白,在猶豫良久之后,我才決定寫一寫 Scala 的這一特點。事實上,我本來沒有打算討論 Scala 的函數功能,但看到一篇有關對象分支(switch)應用的文章后,我想,還是有必要聊聊這個特點。以下內容基本上都來自這篇博客文章:
模式匹配究竟是用來做什么的?它讓你可以將一個值對多種情況(case)進行匹配,有點類似 Java 中的分支(switch)語句。但它不是僅僅匹配數字(這是分支語句的作用),而是用戶能夠對本質上為對象創建形式(creation form)的事物進行匹配。

以下示例也來自上文提到的博客:
```
    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 構造函數,然后進行提取,因此在箭頭右側的表達式中是可用的。

***模式匹配的意義***
*為什么你需要模式匹配?我們都會有復雜的數據。如果我們堅持嚴格的面向對象編程,那么我們就不愿去關注數據樹的內部情況。相反,我們想要調用方法,讓方法來做這些事情。如果我們有能力完成這件事,就不會非常需要模式匹配,因為方法滿足了我們的要求。但是,很多時候對象沒有我們所需的方法,并且我們不能(或不愿)為對象添加新的方法。*
因此,模式匹配被認為是一種獲得擴張性的有效方法,并且,它還為該問題提供了一種不錯的解決方案,訪問者設計模式所導致的冗長除外。
不管怎樣,強烈推薦你看看上面所提到的文章中”擴展性的兩個方向“(Two directions of extensibility)那個小節。

**簡單的 DSL(特定領域語言)**
編寫 DSL,Scala 是一個很好的選擇。事實上,Scala 適用于內部和外部 DSL。在這篇文章中,你可以找到一些使用 Ruby 和 Scala 編寫內部 DSL 的特點比較。下面這篇文章也很棒,是關于使用 Scala 編寫內部 DSL 的:Boolean Algebra Internal DSL in Scala (aka fun with Unicode names )。

此外,對于外部 DSL,Scala 也應該是首選語言,背后的原因是解析器組合子庫(parser combinator lib),它讓為新語言編寫編譯器成為一件很酷的事。
**與 Java 代碼之間的互操作性**
在 JVM 上的實現 Scala 的程序可以無縫地與 Java 平臺整合,很多 Scala 類型實際上都編譯為 Java 類型,因此,用戶可以放心地使用 Java 類型。而且,你也可以混合地使用 JVM 語言來編程,如:JRuby、Groovy、Clojure 等。這里有一篇不錯的文章,提供了這種示例。

**學習型語言**
我有兩個習慣,在 Scala 的學習過程中,我堅持了這兩個習慣:
◆遇到新的技術術語,訪問維基百科,理解更多信息;比如 Function literal(文本函數)、Referentially transparent(引用透明度)、Partial function(偏函數)、Currying(科里華),還有很多其他術語。
◆參考我對其他語言的理解,檢查這些術語的涵義是否實現。
通過一些好的練習,如編寫沒有副作用的純函數,將精力集中在代碼中的“what”部分,而將“how”的部分交給語言處理;這兩個習慣讓我獲得更多知識,也提高了代碼的質量。

**團隊**
Scala 由馬丁·奧德斯基(Martin Odersky)設計,他是瑞士聯邦理工學院洛桑分校(EPFL)編程方法實驗室小組的管理者。奧德斯基曾受雇于 Sun 公司編寫 Java 1.1 編譯器,他還是 Java 1.1 到 Java 1.4 的 Javac 主要開發者。此外,他還是 Java Generics 的提出者。51CTO編輯曾通過電子郵件與奧德斯基就 Scala 的語言特性進行了交流,并得到了回復信件[如下](http://developer.51cto.com/art/200906/128037.htm)。
Scala 現在由奧德斯基和他在瑞士聯邦理工學院洛桑分校的團隊維護。不過,還有其他一些具有才華的開發者參與,通過多年的工作,他們共同打造出了 Scala 這一編程語言。

**來源**
Scala 受到了[多種語言的啟發](http://developer.51cto.com/art/200907/134890.htm):
◆大多數的句法來自 Java 和 C#。
◆其他一些元素也來自 Java,比如:基本類型(basic type)、類庫,以及其運行模型。
◆它所用的統一對象模型是由 Smalltalk 最先使用的。
◆通用嵌套(universal nesting)的理念也出現在 Algol、Simula 中,而且最近還出現在 Beta 和 gbeta 中。
◆函數式編碼的方法在精神上也與 ML 語言家族類似,該語言家族中包含 SML、OCaml,以及最主要的成員 [F#](http://developer.51cto.com/art/200906/130786.htm)。
◆Scala 標準庫中的許多較高階的函數,也出現在 ML 和 Haskell 中。
◆Scala 的隱式參數也是受到 Haskell 類型類的啟發。Scala 基于 actor 的并行庫主要是受到 Erlang 的啟發。
◆將插入(infix)操作符視為函數,以及允許文本函數(或 block 區塊)作為參數,以使庫能夠定義控制結構,這些特定的理念可以回溯至 Iswim 和Smalltalk。

**專家觀點**
實際上,像詹姆士·斯特拉坎(James Strachan:編程語言 Groovy 的創始人)的[這樣的言語](http://developer.51cto.com/art/200907/134785.htm)讓人感到有點驚喜:
說實話,如果有人在 2003 年給我一本由馬丁·奧德斯基(Martin Odersky)、萊克斯·斯彭(Lex Spoon)和比爾·文納斯(Bill Venners)合著的《Programming in Scala》,我很可能不會再去創建 Groovy。

在結束之前,我做一下總結:
我喜歡 Scala,因為它是高效的、學習型的語言,具有較好的并行模型,以及非常適用于編寫 DSL。

原文:Scala is my next choice

作者:khelll 譯者:[司馬牽牛](http://tsaizb.blog.51cto.com/)

【相關閱讀】
[Scala取代Java?可能嗎?熱議仍持續不斷
](http://developer.51cto.com/art/200907/135770.htm)
[Groovy創始人:Java面臨終結 Scala將取而代之
](http://developer.51cto.com/art/200907/134785.htm)
[Scala如何改變了我的編程風格:從命令式到函數式
](http://developer.51cto.com/art/200906/127825.htm)
[Scala的類型系統 比Java更靈活
](http://developer.51cto.com/art/200906/126710.htm)
[Java程序員,你為什么要關注Scala
](http://developer.51cto.com/art/200905/125526.htm)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容