Scala是什么?可伸展的語言!
Scala是什么
Scala語言的名稱來自于“可伸展的語言”。之所以這樣命名,是因為他被設(shè)計成隨著使用者的需求而成長。你可以把Scala應(yīng)用在很大范圍的編程任務(wù)上,從寫個小腳本到建立個大系統(tǒng)。
51CTO編輯推薦:Scala編程語言專題
Scala是很容易進(jìn)入的語言。它跑在標(biāo)準(zhǔn)的Java平臺上,可以與所有的Java庫實現(xiàn)無縫交互。它也是用來編寫腳本把Java控件鏈在一起的很好的語言。但是用它來建立大系統(tǒng)和可重用控件的架構(gòu)將更能夠發(fā)揮它的力量。
從技術(shù)層面上來說,Scala是一種把面向?qū)ο蠛秃瘮?shù)式編程理念加入到靜態(tài)類型語言中的混血兒。Scala的許多不同的方面都展現(xiàn)了面向?qū)ο蠛秃瘮?shù)式編程的熔合;或許它比其他那些廣泛使用的語言更有滲透性。在可伸展性方面,這兩種編程風(fēng)格具有互補(bǔ)的力量。Scala的函數(shù)式編程使得它便于快速地從簡單的碎片開始建立一些有趣的東西。它的面向?qū)ο筇匦杂质顾阌跇?gòu)造大型系統(tǒng)并使它們適應(yīng)于新的需求。Scala中這兩種風(fēng)格的組合使得它有可能表達(dá)新的編程模式和控件抽象。并產(chǎn)生了易讀、簡潔的編程風(fēng)格。由于它良好的延展性,用Scala編程將會有很多的樂趣。
不同尺寸的程序傾向于需要不同的編程結(jié)構(gòu)。舉例來說,考慮以下的Scala程序:
- var capital = Map("US"->"Washington", "France" -> "Paris")
- capital += ("Japan" -> "Tokyo")
- println(capital("France"))
這段程序建立了一個國家和它們的首都之間的映射表,增加了一個新的綁定("Japan"->"Tokyo"),然后打印了與法國相關(guān)的首都。 本例中的聲明都是高層次的,也就是說,沒有被外加的分號或者類型注釋弄得亂糟糟的。實際上,這種感覺就好像那種現(xiàn)代的“腳本化”語言,比如,Perl,Python或者Ruby。這些語言的一個普遍特征,與上例有關(guān)的,就是它們都在語法層面上支持“關(guān)聯(lián)映射”。
關(guān)聯(lián)映射非常有用,因為它能讓程序易讀和清晰。然而,有些時候你或許不贊成它們的這種“均碼”哲學(xué),因為你需要用一種更加細(xì)粒度地去控制在你程序中用到的映射的屬性。Scala可以在你需要的時候提供這種細(xì)粒度的控制,因為映射在Scala里并不是語法特性。它們是庫抽象,你可以擴(kuò)展或者改造。
在上面的程序里,你將獲得一個缺省的Map實現(xiàn),不過你也可以很輕松地改變它。比方說,你可以定義個特別的實現(xiàn),如HashMap或TreeMap,或者你可以特定這個映射必須是線程安全的,混入:mix-in個SynchronizedMap特色:trait。你還可以給映射特定一個缺省值,或你可以重載你創(chuàng)建的映射的任意方法。每個例子里,你都可以如上例所示那樣使用同樣簡單的映射訪問語法。
這個例子顯示了Scala帶給你的方便性和靈活性,可以讓你更好的了解Scala是什么。Scala有一整套的方便構(gòu)件來幫助你快速啟動及讓你用一種愉悅清晰的狀態(tài)編程。與此同時,你有信心你不會讓語言過度發(fā)育。你總可以把程序按你的需要裁剪,因為所有的東西都是基于庫模塊的,可以依照需要選擇和修改。
Scala是什么:培育新的類型
Eric Raymond把大教堂和雜貨鋪?zhàn)鳛檐浖_發(fā)的兩個隱喻。 大教堂是幾近于完美的建筑物,要花很長的時間建設(shè)。一旦建成了,就長時間保持不變。相對來說,雜貨鋪則天天在被工作其中的人調(diào)整和擴(kuò)展。Raymond的文章中,雜貨鋪是對于開源軟件開發(fā)的隱喻。Guy Steele在他的講話“發(fā)展一門語言”中提到同樣的差別也可以應(yīng)用在語言定義中。 Scala更像一個雜貨鋪而不是大教堂,因為它被設(shè)計為讓用它編程的人擴(kuò)展和修改的。Scala并沒有提供所有你在一種“完美齊全”語言中可能需要的東西,而是把制作這些東西的工具放在了你的手中。
這兒有個例子。許多程序需要一個能夠變得任意大都不會溢出或者由于數(shù)學(xué)操作而“繞回”的整數(shù)類型。Scala在庫類Scala.BigInt中定義了這樣一個類型。這里有一個使用了那個類型的方法定義,用以計算傳入整數(shù)的階乘值:
- def factorial(x: BigInt): BigInt =
- if (x == 0) 1 else x * factorial(x - 1)
現(xiàn)在,如果你調(diào)用了factorial(30),你將得到:
265252859812191058636308480000000
BigInt看上去就像一個內(nèi)建的類型,因為你可以使用整數(shù)值和這種類型值的操作符如*和-。然而它只是湊巧定義在Scala標(biāo)準(zhǔn)庫中的類。 如果這個類缺失了,可以直接由任意的Scala程序員寫一個實現(xiàn)出來,舉例來說,通過包裝Java的類java.math.BigInteger(實際上,Scala的BigInt就是這么實現(xiàn)的)。
當(dāng)然,你也可以直接使用Java的類庫。但結(jié)果卻不盡樂觀,因為盡管Java允許創(chuàng)建新的類,但這些類總感覺不像原生的語言支持。
- import java.math.BigInteger
- def factorial(x:BigInteger): BigInteger =
- if (x == BigInteger.ZERO)
- BigInteger.ONE
- else
- x.multiply(factorial(x.subtract(BigInteger.ONE)))
BigInt代表了許多其他類似于數(shù)字的類型——大十進(jìn)制數(shù),復(fù)數(shù),分?jǐn)?shù),置信區(qū)間,多項式——諸如此類。一些編程語言原生實現(xiàn)了其中的一些類型。舉例來說,Lisp,Haskell和Python實現(xiàn)了大整數(shù);Fortran和Python實現(xiàn)了復(fù)數(shù)。但是任何語言想要嘗試同時實現(xiàn)所有的這些抽象類型將很容易變得太大而難以管理。更進(jìn)一步,即使如果有這樣的語言,總有些應(yīng)用會使用其他的沒支持的數(shù)字類型。所以嘗試在一種語言里提供所有東西的解決之道不可能很好地伸展。取而代之,Scala允許用戶在他們需要的方向上通過定義易用庫來發(fā)展和改造語言,使得這些特性感覺上好像原生語言支持一樣。
培育新的控制結(jié)構(gòu)
前面的例子演示了Scala讓你增加新的類型,使得它們用起來方便得像內(nèi)建類型一樣。同樣的擴(kuò)展理念也應(yīng)用在控制結(jié)構(gòu)上。這種類型的擴(kuò)展是由Scala的“基于行動類”的并發(fā)編程API闡明的。
隨著近年多核處理器的激增,為了獲取可接受的性能,你將必須在應(yīng)用中運(yùn)用更多的并行機(jī)制。常常這就意味著重寫你的代碼來讓計算分布到若干并發(fā)線程上。不幸的是,創(chuàng)建依賴性的多線程程序在實踐中被證明是非常具有挑戰(zhàn)性的。Java的線程模型是圍繞著共享內(nèi)存和鎖建立的,尤其是當(dāng)系統(tǒng)在大小和復(fù)雜度都得到提升的時候,這種模型常常是不可理喻的。很難說程序里面沒有資源競爭或潛藏的死鎖——有些東西不是能在測試?yán)锩鏅z驗得出,而或許只在投入生產(chǎn)后才表現(xiàn)出來。而大致可以認(rèn)為比較安全的可選方案是消息傳遞架構(gòu),例如在Erlang編程語言中應(yīng)用的“行動類”方案。
Java伴隨著一個豐富的,基于線程的并發(fā)庫。Scala可以像其他JavaAPI那樣使用它編程。然而,Scala也提供了一個實質(zhì)上實現(xiàn)了Erlang的行動類模型的附加庫。
行動類是能夠?qū)崿F(xiàn)于線程之上的并發(fā)抽象。它們通過在彼此間發(fā)送消息實現(xiàn)通信。每個行動類都能實現(xiàn)兩個基本操作,消息的發(fā)送和接受。發(fā)送操作,用一個驚嘆號表示,發(fā)送消息給一個行動類。這里用一個命名為recipient的行動類舉例如下:
- recipient ! msg
發(fā)送是異步的;就是說,發(fā)送的行動類可以在一瞬間完成,而不需要等待消息被接受和處理。每一個行動類都有一個信箱:mailbox把進(jìn)入的消息排成隊列。行動類通過receive代碼塊處理信箱中受到的消息:
- receive {
- case Msg1 => ... // handle Msg1
- case Msg2 => ... // handle Msg2
- // ...
- }
接收代碼塊由許多case語句組成,每一個都用一個消息模板查詢信箱。信箱中第一個符合任何case的消息被選中,并且執(zhí)行相應(yīng)的動作。如果信箱中不含有任何符合任何case的消息,行動類將休眠等待新進(jìn)的消息。
這里舉一個簡單的Scala行動類實現(xiàn)檢查值(cheksum)計算器服務(wù)的例子:
- actor {
- var sum = 0
- loop {
- receive {
- case Data(bytes) => sum += hash(bytes)
- case GetSum(requester) => requester ! sum
- }
- }
- }
這個行動類首先定義了一個名為sum的本地變量,并賦了初值為零。然后就用receive段落重復(fù)等待在消息循環(huán)中。如果收到了Data消息,就把發(fā)送的bytes取哈希值加到sum變量中。如果收到了GetSum消息,就用消息發(fā)送requester!sum把當(dāng)前sum值發(fā)回給requester。requester字段嵌入在GetSum消息里;它通常指出創(chuàng)建請求的行動類。
目前我們并不指望你能完全明白行動類例子。實際上,對于可伸展性這個話題來說這個例子里面最重要的是,不論是actor還是loop還是receive還是發(fā)送消息的符號“!”,這些都不是Scala內(nèi)建的操作符。盡管actor,loop和receive看上去或者表現(xiàn)上都如此接近于控制結(jié)構(gòu)如while或者for循環(huán),實際上它們是定義在Scala的行動類庫里面的方法。同樣,盡管“!”看上去像是個內(nèi)建的操作符,它也不過是定義在行動類庫里面的方法。所有這四個構(gòu)件都是完全獨(dú)立于Scala語言的。
receive代碼塊和發(fā)送“!”語法讓Scala看上去更像Erlang里的樣子,但是在Erlang里面,這些構(gòu)件是內(nèi)建在語言中的,Scala還實現(xiàn)了Erlang其他并發(fā)編程構(gòu)件的大多數(shù),諸如監(jiān)控失敗行動類和超時類。總體來說,行動類已變成表達(dá)并發(fā)和分布式計算的非常好的辦法。盡管它們是定義在庫里的,給人的感覺就像行動類是Scala語言整體的部分。
本例演示了你可以向新的方向“培養(yǎng)”Scala語言乃至像并發(fā)編程這樣的特性。前提是,你需要一個好的架構(gòu)和程序員來做這樣的事。但重要的事情是這的確可行——你可以在Scala里面設(shè)計和實現(xiàn)抽象結(jié)構(gòu),從而快速投入新的應(yīng)用領(lǐng)域,卻仍然感覺像是原生的語言支持。
本文節(jié)選自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻譯的《Programming in Scala》的第一章。
【相關(guān)閱讀】