Scala學習路線

這是一篇為公司內部”scala熱情workshop”活動準備的文章,面向Scala初學者,目的在于幫助大家能盡早就建立起對Scala的整體認識,少走彎路。當然由于水平有限,有些地方可能不準確,不過如果能促進大家多思考多求證,也算達到了目的。

我最開始接觸Scala到現在竟然已經過了4年半了。想到自己現在的scala水平,還處于入門后剛有點感覺的狀態,感覺十分的慚愧。這四年多,一直斷斷續續的學習,中間多次放棄又多次拿起,就像是一本厚書,每次都是從頭看了幾十頁便放下,多次之后,看到的還是前幾十頁。

我之前的學習完全是自學,靠自己摸索,在沒有人指導的情況下,走了很多彎路。其中最大的阻礙,有兩點:

自己在編程方面的知識儲備不夠,太多的東西需要現學,有時候甚至意識不到自己該學
對scala的定位和認識不清楚,常常在錯誤的方向上努力直到最后撞墻,而一些重要的知識卻總在回避,導致學習過程特別的痛苦

所以我想把其中一些重要的東西記錄下來,讓和我一樣正在學習scala的同學能多一些思考,少走一些彎路。



剛接觸Scala的同學,看到的基本上都是一些宣傳性的文章,也因此對它有一些不太正確的觀點。
Scala很簡單,Scala=Java+語法糖
在Scala群里,經常看到一些初學者在聊天的時候,說Scala不過是給Java增加了一些語法糖,讓我們寫代碼的時候可以方便一些而已。他們很喜歡這些語法糖,因為寫出來的代碼看起來不像Java那么繁瑣。在經過一兩周的學習以后,他們認為自己已經掌握了Scala,覺得它很簡單,于是跑到別的群里吹噓,忽悠更多的人來學Scala。
的確,Scala中有一些東西看起來的確很簡單,只需要把<<scala編程>>或者<<快學Scala>>這樣的書大概翻過一遍,就差不多能用了。比如:
分號可選
多行字符串
val
object
trait
pattern matching
類型推斷
map/filter/flatMap
for表達式
implicit

看起來,學習Scala就像爬樓梯一樣,哪有什么學習曲線?



當我們基本掌握上面那些知識點之后,也許一些基本的Scala開發還能勉強勝任,但是我們很快就會發現很多別人寫的Scala代碼我們看不懂,別人討論的Scala知識我們也看不懂,我們只是在把Scala當Java用。為什么?
因為那些真正讓Scala具有吸引力、有難度的地方不在上面。比如:
類型系統
函數式編程
Monad

也許是因為那些書面向的都是初學者,在這些方面都講得比較簡略,點到即止。此時如果看<<Scala in Depth>>這本書,基本上很難看得下去。
當我們決定把這些都學會的時候,會驚訝的發現,難度變成了這樣:


因為我們會發現,自己缺少了太多的背景知識,需要先補很多東西,甚至學一門別的函數式語言之后,才能回來學Scala。
Groovy創始人曾經說過:如果Scala早點出來,我就不會搞groovy了

不太清楚Groovy的創始人在什么場合說了這句開玩笑的話,但有些人就信以為真,經常在群里喊:都來學Scala吧,還學什么groovy啊,你沒聽說那誰都這么說了。
然而對這兩種語言有所了解的人都知道,這兩門語言就沒什么可比性。
Scala是一門靜態類型,結合了面向對象和函數式的,擁有強大的類型系統的語言。而Groovy是一門同時提供了動態類型和靜態類型,基本兼容Java語法的語言。
這兩者的特性、適合場景都不一樣。
Java中能做到的事,Scala都能做并且做的更好
通過各種資料介紹,我們感覺scala比Java要強大的多,有時候會認為,只要Java中能做到的事,在Scala中都能做并且做的更好。
也許很多情況下是這樣,但是有時候不是,比如類似ORM這樣的庫。
在Java中,有hibernate或者跟它類似的,比如我最喜歡的 Ebean 。它們利用字節碼操作,在背后為我們提供了很多增強的功能,讓我們建好model再CRUD非常輕松。雖然它們在后面有一些看不到的魔術操作,但是可以讓我們寫出很干凈簡潔的代碼。
然而這些庫在scala中,要么用起來非常別扭,要么有一些奇怪的問題。而Scala原生的庫,比如squeryl,slick等,都有“多利用類型系統,少做魔術”的追求,所以用起來總是不那么好用。
再舉一個playframework的例子。曾經的playframework1是java版的,現在的playframework2用scala重寫了,雖然名字相同,但是兩者的風格有很大不同。play1有很多魔術,但是代碼簡潔、開發效率極高;而play2 typesafe了,對于普通網站的開發,它的開發效率大大降低了。
Scala和Java互操作很簡單

Scala運行于JVM上,可以與Java互操作。我們甚至在同一個項目中,可以既有Java代碼,又有scala代碼。這是不是意味著,我們可以讓項目中的一部分代碼使用Java實現,另一部分使用Scala?
在理論上是可以的,并且在實際中,有的時候我們不得不這樣。但是混用的過程還是比較痛苦的:
很多類,特別是集合類,Java與Scala各有一套,我們需要不停轉換
Java與Scala類型系統不完全相同,有時候會遇到奇怪的編譯錯誤
有些java庫會對javabean的getter/setter進行字節碼增強,而scala對這種風格支持不好
Scala的類名方法名,有可能在java中看起來很奇怪
過程式與函數式之間的風格沖突

所以我通常會采用純java或者純scala方案,除非很有必要,并且兩者可以分離得很清楚,并且之間只用很少的接口進行交互。
Java里有一些很好的庫想在scala使用,人們通常都會先寫一個wrapper,在外面包上一層scala接口。但是如果包的不好,用起來也是非常麻煩,不如不用,比如: http://scalafx.io
Java程序員學習Scala最容易


初看起來Scala的語法跟Java比較像,對于Java程序員比較友好,所以很多人認為Java程序員學習Scala最容易。然而對于Java程序員來說,如果以前沒有接觸過函數式編程,對于類型系統了解不多的話,到后期會面臨巨大的壓力,因為有太多與函數式及類型系統相關的概念需要學習,而這往往不是短期內就能掌握的。甚至有可能為了學習scala而中途專門去學習另一門函數式語言(如haskell, lisp等),掌握了那些概念后,再回來看scala。
Scala是一門過程式與函數式結合的語言,Scala代碼中,過程式的代碼經常與函數式代碼混在一起,所以利用它來學習,常常會讓人迷惑。而且在scala資料中,專門講函數式知識的并不多。而像Hashell這樣的純函數式語言,幾乎所有的資料都會講到函數式編程里的各種概念,更加利于學習。
所以Scala對于從函數式語言的程序員來說,可能學習曲線更小一些。
函數式很簡單

對于像我這樣的Java程序員來說,函數式編程是一個很神秘的話題。從前以為,像Java/C這樣的過程式語言的編程方式,就是全部,想不出除此之外還能有什么編程方式。所以對于“函數式”,我們往往通過一兩個簡單的定義會想像,然后得到“函數式編程”很簡單的結論。
比如,關于函數式編程,有人說它有兩個重要的特點:
追求不變性和無副作用
函數作為一等公民,它可以當作值一樣定義、傳遞

然后我們想,我已經做到了盡量用val不用var,也不在方法里中做一些有副作用的操作,比如打印輸出。然后,我還喜歡用那些好用的map之類的函數,直接寫一個匿名函數給它,這不是符合了第二條嗎?這樣看來,函數式編程好像很簡單啊。
這種想法,就跟我們會用class定義類了,然后就說自己會“面向對象”了一樣。
由于我也剛剛開始學習函數式編程,沒法給出準確的描述,只能大概說一些:在純函數式編程中不能使用像 for
循環這樣的語法,也不能給一個變量重新賦值,所以它解決問題的思路跟我們在過程式語言中做的,有很大不同。比如遞歸的大量使用,比如函數的組合,比如monad的概念,很多都是我之前從來沒有見過的。在學習一門純函數式語言的過程中,我們會發現以前的編程經驗用不上了,經常有種寸步難行、有力無處使的感覺。而且會經常遇到各種數學相關的概念,還得不斷補數學知識。
從一門過程式語言轉向另一門過程式語言很快,可能要熟悉的就是語法、類庫、一些最佳實踐等,一兩周可能就差不多了。但是從一門過程式語言轉向一門函數式語言,可能要花幾個月的時間。前者像是搬到了新城市,后者像是移民到了新國家。
(這一塊要等我掌握了一門函數式語言之后再來補充)
Scala的DSL很強大



由于Scala強大的類型系統和它的語法支持,我們可以設計出強大的類型安全的DSL。
比如像scalatest這樣的庫,可以讓我們這樣寫:
list should have length 3

這樣看起來像是一句普通英語。
但Scala的DSL有兩點需要注意:
它的特點是類型安全。如果以表達能力看,它比動態語言要弱要難看。可以通過查看sbt和gradle的構建文件來獲取直觀感受
對類型系統方面的能力要求高。以scalatest為例,如果沒有熟悉、深刻地掌握scala類型系統,很難設計出來這樣的DSL。而在動態語言中就沒有這個門檻

所以個人感覺,scala中DSL的“強大”主要體現在類型方面,而在表達能力和易讀性方面,可能要弱于其它一些語言。
類型系統我們不用學
Scala中的類型系統很強大,也很復雜,對于初學者來說是一個難點。記得以前看到有人說,普通的程序員?不需要掌握它們,只有類庫的設計者需要掌握。
但是實際情況是,如果不能盡早的掌握足夠的類型系統知識,在使用Scala時我們幾乎寸步難行。我們在編譯Scala代碼時,遇到的最多錯誤就是各種類型不匹配,如果不熟悉的話,可能要卡幾個小時都解決不了。
所以在最開始學習的時候,就不能回避它。也許我們的目的不是設計出一個類型很復雜的類庫,我們也要能做到看得懂復雜一點的方法簽名,解決常見的類型編譯錯誤。
我公司有個新項目,我想用Scala,邊學邊用
很多人低估了Scala的學習難度,甚至剛開始學習時,便打算在公司的新項目上使用。或者在自己也沒有熟練掌握的情況下,便向團隊中強推Scala,這種做法是十分危險的。
Scala中關于函數式與類型系統方面的知識,對團隊成員的要求比較高。如果是一個在這些方面不熟悉的團隊,想在短期內掌握并且用好它們,基本上是一件不可能的任務。
另外,Scala的IDE支持、資料文檔、生態圈等,相對于Java這種成熟的語言還比較弱,這對于新人也是一個比較大的障礙。
我覺得,只有當團隊中已經有對Scala熟練的人,團隊成員學習能力較強,并預留了大量學習時間的情況下,才可以嘗試使用Scala來做項目。

為什么要學Scala

有不少人問過我這個問題:你為什么要學習Scala?
在最開始,我也是被各種宣傳忽悠了,以為Scala是一門簡單的,對Java有很多改進的,甚至可能很快就成為下一代Java的語言。當時正打算做一個網站,于是就用它邊學邊做。幾個月后,實在無法忍受它的編譯速度、各種類庫的缺失、以及各種各樣的編譯錯誤,放棄了它。
但是當時創建的那個Scala群里,卻有非常好的交流氛圍。很多人由于對Scala很有興趣,在群里討論各種新鮮好玩的技術,讓我大開眼界,發現Java原來只是一個小世界。另外在群里認識了楊云,也因此加入了TW,現在他是我的sponsor。
來到我們公司之后,居然有兩次參與Scala項目的機會。發現我們的客戶居然都喜歡用Scala,西安辦公室里也有越來越多的人主動或者被迫學習Scala。由于現在有了更多學習的時間,所以我又把它撿了起來,同時驚喜地發現之前一直沒搞懂的問題,現在竟然差不多理解了。
我認為我現在學習Scala的原因是:它為我打開了編程世界的一扇門,讓我看到了與之前完全不同的世界。通過對它的學習,我可以強迫自己學習更多編程知識,提高自己的能力,從而有機會跟更多牛人交流。另外,我個人也看好Scala的未來,所以認為越早掌握它對自己越有利。
Scala學習路線
結合我自己的學習經歷,我把Scala的學習按難度分成了幾塊。每一塊的難度側重點相對獨立,需要一段時間的專門學習。
第一塊:語法糖
第一塊是學習Scala的各種基本特性,比如object, trait, pattern matching等,這些知識對于一個熟練的Java程序員來說,沒有太大難度。看完一本Scala書,或者參與一個不太復雜的Scala項目,基本上就可以到達。
推薦書籍: << Scala編程>> 或者 <<快學Scala>>
文章:Daily Scala: http://daily-scala.blogspot.com/
開源項目: https://github.com/inca/circumflex/tree/master/web
Circum-web是一個比較簡單的scala mvc框架,代碼簡單、注釋豐富。由于它沒有用到多少函數式風格的代碼,對于初學者來說,還是比較容易上手的。雖然現在用它的人不多,但不失為一個很好的學習資源。同時它還有circum-orm等項目,也可以用來學習。
第二塊:類型系統
此時最困擾我們的,應該就是各種各樣類型相關的編譯錯誤,以及一些復雜難懂的類型聲明。
特別是這幾個方面:
路徑依賴類型
非變,協變,逆變
Type class
高階類型等

推薦書籍: <<Scala in Depth>>

博客文章:
http://hongjiang.info/scala/ 中有關類型系統的文章
http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/

參考語言:Haskell中的類型系統
第三塊:函數式
這一塊的目標就是搞懂monad,以及各種函數式編程中提到的概念。這里可能要結合其它函數式語言學習。
推薦書籍: <<Scala in Depth>>
<<functional programming in scala>>

推薦博客:
http://hongjiang.info/scala/ 中關于monad的文章
Manods are elephants系列: http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html
論面向組合子程序設計方法: http://www.blogjava.net/ajoo/category/6968.html
Learning scalaz: http://eed3si9n.com/learning-scalaz/

推薦庫:scalaz
推薦語言:Haskell
第四塊:生態
前三塊基本上都是語言層面,這一塊是庫,比如一些我們經常用到的,或者scala中一些很有名的庫:
構建工具: sbt
scalatest/specs2
scalaz
akka
spark

這里要根據項目和興趣進行選擇。
第五塊:其它
Scala中的一些其它特性,比如:
Dynamic: http://stackoverflow.com/questions/15799811/how-does-type-dynamic-work-and-how-to-use-it
macro
scala.js, jsscala

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容