Freewind @ Thoughtworks scala java javascript dart 工具 编程实践 月结 math python english [comments admin] [feed]

(2014-06-14) Scala热情workshop: 2. Scala学习路线(1) 初学者的观点

广告: 云梯:翻墙vpn (省10元) 土行孙:科研用户翻墙http proxy (有优惠)

这是一篇为公司内部”scala热情workshop”活动准备的文章,面向Scala初学者,目的在于帮助大家能尽早就建立起对Scala的整体认识,少走弯路。当然由于水平有限,有些地方可能不准确,不过如果能促进大家多思考多求证,也算达到了目的。

我最开始接触Scala是2010年初,到现在竟然已经过了4年半了。想到自己现在的scala水平,还处于入门后刚有点感觉的状态,感觉十分的惭愧。这四年多,一直断断续续的学习,中间多次放弃又多次拿起,就像是一本厚书,每次都是从头看了几十页便放下,多次之后,看到的还是前几十页。

我之前的学习完全是自学,靠自己摸索,在没有人指导的情况下,走了很多弯路。其中最大的阻碍,有两点:

  1. 自己在编程方面的知识储备不够,太多的东西需要现学,有时候甚至意识不到自己该学
  2. 对scala的定位和认识不清楚,常常在错误的方向上努力直到最后撞墙,而一些重要的知识却总在回避,导致学习过程特别的痛苦

所以我想把其中一些重要的东西记录下来,让和我一样正在学习scala的同学能多一些思考,少走一些弯路。

刚接触Scala的同学,看到的基本上都是一些宣传性的文章,也因此对它有一些不太正确的观点。

Scala很简单,Scala=Java+语法糖

在Scala群里,经常看到一些初学者在聊天的时候,说Scala不过是给Java增加了一些语法糖,让我们写代码的时候可以方便一些而已。他们很喜欢这些语法糖,因为写出来的代码看起来不像Java那么繁琐。在经过一两周的学习以后,他们认为自己已经掌握了Scala,觉得它很简单,于是跑到别的群里吹嘘,忽悠更多的人来学Scala。

的确,Scala中有一些东西看起来的确很简单,只需要把<<scala编程>>或者<<快学Scala>>这样的书大概翻过一遍,就差不多能用了。比如:

  1. 分号可选
  2. 多行字符串
  3. val
  4. object
  5. trait
  6. pattern matching
  7. 类型推断
  8. map/filter/flatMap
  9. for表达式
  10. implicit

看起来,学习Scala就像爬楼梯一样,哪有什么学习曲线?

当我们基本掌握上面那些知识点之后,也许一些基本的Scala开发还能勉强胜任,但是我们很快就会发现很多别人写的Scala代码我们看不懂,别人讨论的Scala知识我们也看不懂,我们只是在把Scala当Java用。为什么?

因为那些真正让Scala具有吸引力、有难度的地方不在上面。比如:

  1. 类型系统
  2. 函数式编程
  3. 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?

在理论上是可以的,并且在实际中,有的时候我们不得不这样。但是混用的过程还是比较痛苦的:

  1. 很多类,特别是集合类,Java与Scala各有一套,我们需要不停转换
  2. Java与Scala类型系统不完全相同,有时候会遇到奇怪的编译错误
  3. 有些java库会对javabean的getter/setter进行字节码增强,而scala对这种风格支持不好
  4. Scala的类名方法名,有可能在java中看起来很奇怪
  5. 过程式与函数式之间的风格冲突

所以我通常会采用纯java或者纯scala方案,除非很有必要,并且两者可以分离得很清楚,并且之间只用很少的接口进行交互。

Java里有一些很好的库想在scala使用,人们通常都会先写一个wrapper,在外面包上一层scala接口。但是如果包的不好,用起来也是非常麻烦,不如不用,比如: http://scalafx.io

Java程序员学习Scala最容易

初看起来Scala的语法跟Java比较像,对于Java程序员比较友好,所以很多人认为Java程序员学习Scala最容易。然而对于Java程序员来说,如果以前没有接触过函数式编程,对于类型系统了解不多的话,到后期会面临巨大的压力,因为有太多与函数式及类型系统相关的概念需要学习,而这往往不是短期内就能掌握的。甚至有可能为了学习scala而中途专门去学习另一门函数式语言(如haskell, lisp等),掌握了那些概念后,再回来看scala。

Scala是一门过程式与函数式结合的语言,Scala代码中,过程式的代码经常与函数式代码混在一起,所以利用它来学习,常常会让人迷惑。而且在scala资料中,专门讲函数式知识的并不多。而像Haskell这样的纯函数式语言,几乎所有的资料都会讲到函数式编程里的各种概念,更加利于学习。

所以Scala对于从函数式语言的程序员来说,可能学习曲线更小一些。

函数式很简单

对于像我这样的Java程序员来说,函数式编程是一个很神秘的话题。从前以为,像Java/C这样的过程式语言的编程方式,就是全部,想不出除此之外还能有什么编程方式。所以对于“函数式”,我们往往通过一两个简单的定义会想像,然后得到“函数式编程”很简单的结论。

比如,关于函数式编程,有人说它有两个重要的特点:

  1. 追求不变性和无副作用
  2. 函数作为一等公民,它可以当作值一样定义、传递

然后我们想,我已经做到了尽量用val不用var,也不在方法里中做一些有副作用的操作,比如打印输出。然后,我还喜欢用那些好用的map之类的函数,直接写一个匿名函数给它,这不是符合了第二条吗?这样看来,函数式编程好像很简单啊。

这种想法,就跟我们会用class定义类了,然后就说自己会“面向对象”了一样。

由于我也刚刚开始学习函数式编程,没法给出准确的描述,只能大概说一些:在纯函数式编程中不能使用像for循环这样的语法,也不能给一个变量重新赋值,所以它解决问题的思路跟我们在过程式语言中做的,有很大不同。比如递归的大量使用,比如函数的组合,比如monad的概念,很多都是我之前从来没有见过的。在学习一门纯函数式语言的过程中,我们会发现以前的编程经验用不上了,经常有种寸步难行、有力无处使的感觉。而且会经常遇到各种数学相关的概念,还得不断补数学知识。

从一门过程式语言转向另一门过程式语言很快,可能要熟悉的就是语法、类库、一些最佳实践等,一两周可能就差不多了。但是从一门过程式语言转向一门函数式语言,可能要花几个月的时间。前者像是搬到了新城市,后者像是移民到了新国家。

(这一块要等我掌握了一门函数式语言之后再来补充)

Scala的DSL很强大

由于Scala强大的类型系统和它的语法支持,我们可以设计出强大的类型安全的DSL。

比如像scalatest这样的库,可以让我们这样写:

list should have length 3

这样看起来像是一句普通英语。

但Scala的DSL有两点需要注意:

  1. 它的特点是类型安全。如果以表达能力看,它比动态语言要弱要难看。可以通过查看sbt和gradle的构建文件来获取直观感受
  2. 对类型系统方面的能力要求高。以scalatest为例,如果没有熟悉、深刻地掌握scala类型系统,很难设计出来这样的DSL。而在动态语言中就没有这个门槛

所以个人感觉,scala中DSL的“强大”主要体现在类型方面,而在表达能力和易读性方面,可能要弱于其它一些语言。

类型系统我们不用学

Scala中的类型系统很强大,也很复杂,对于初学者来说是一个难点。记得以前看到有人说,普通的程序员不需要掌握它们,只有类库的设计者需要掌握。

但是实际情况是,如果不能尽早的掌握足够的类型系统知识,在使用Scala时我们几乎寸步难行。我们在编译Scala代码时,遇到的最多错误就是各种类型不匹配,如果不熟悉的话,可能要卡几个小时都解决不了。

所以在最开始学习的时候,就不能回避它。也许我们的目的不是设计出一个类型很复杂的类库,我们也要能做到看得懂复杂一点的方法签名,解决常见的类型编译错误。

我公司有个新项目,我想用Scala,边学边用

很多人低估了Scala的学习难度,甚至刚开始学习时,便打算在公司的新项目上使用。或者在自己也没有熟练掌握的情况下,便向团队中强推Scala,这种做法是十分危险的。

Scala中关于函数式与类型系统方面的知识,对团队成员的要求比较高。如果是一个在这些方面不熟悉的团队,想在短期内掌握并且用好它们,基本上是一件不可能的任务。

另外,Scala的IDE支持、资料文档、生态圈等,相对于Java这种成熟的语言还比较弱,这对于新人也是一个比较大的障碍。

我觉得,只有当团队中已经有对Scala熟练的人,团队成员学习能力较强,并预留了大量学习时间的情况下,才可以尝试使用Scala来做项目。

comments powered by Disqus