这是一篇为公司内部”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