Scala: 感觉像动态的静态语言

Weblogs Forum - Scala: The Static Language that Feels Dynamic

著名的"Thinking in Java"作者Bruce Eckel 在artima.com写了这篇Scala入门篇(banq:artima的论坛和Jdon的论坛都源于同一个宗主Jive,所以你觉得两者界面很象)。

下面大概意译一下Bruce Eckel这篇文章:
Bruce Eckel 认为scala虽然是静态语言,但感觉非常像Python,
(Bruce Eckel用语很感性,不象Thinking,而象feeling)。


class Building
val b = new Building

val是不可变量,这样能够使得并发编码更容易一些(var也有变量的).这里没有必要在b之前象Java那样声明一下类型,因为scala能够分辨出类型。



class Building(feet: Int) {
val squareFeet = feet
}
val b = new Building(100)
println(b.squareFeet)

println打印输出不必使用类似Java的System.out了,类中字段缺省是public的,这样我们可以使用b.squareFeet直接访问其类中字段。当然你也可以设置private。


class Building(val feet: Int)
val b = new Building(100)
println(b.feet)

这段和上面区别是b.feet, feet能够自动成为类的字段。



case class Building(feet: Int)
val b = Building(100)
println(b) // Result: Building(100)

这段代码主要是开始的case class ,参数自动成为字段,参数之前也不用加val了。


val m = Map(Building(5000) -> "Big", Building(900) -> "Small", Building(2500) -> "Medium")
m(Building(900))
// Result: Small

scala中使用->来表示map的键和值。



class House(feet: Int) extends Building(feet)
val h = new House(100)
println(h.feet) // Result: 100

展示了extends继承,但是注意extends后面的父类写法。



trait Bathroom
trait Kitchen
trait Bedroom {
def occupants() = { 1 }
}
class House(feet: Int) extends Building(feet) with Bathroom with Kitchen with Bedroom
var h = new House(100
val o = h.occupants()
val feet = h.feet

traits是将行为混合在一起,类似过去的AOP等,被认为用来实现DCI架构较好的语言方式,这里的trait非常类似Java中的接口,但比接口要丰富,可以自己定义方法occupants,其def是用来定义方法名称的,等于号后面是方法体。

def occupants() = { 1 }表示返回值int类型1,如果要更加规范如下:
def occupants(): Int = { 1 }

注意occupants已经成为House的一部分,这就是traits的mixin力量,而且这个mixin的性能要比Java好多。

以上是简单的语法。

函数编程方式functional programming
比如我们有一个集合数据:


val v = Vector(1.1, 2.2, 3.3, 4.4)

通过下面语法可以输出打印:
for(n <- v) {
println(n)
}

注意:<- 前面的n能够得到v集合中每个值。

还有更简单的:v.foreach(println) (Java中参数一般是变量,不会是方法等,这里是方法println)

别看这么简单,它是利用的函数编程的优点,实际是一个匿名函数,完整的基本形式:


( function parameters ) => function body

=>意味着:将左边的参数应用到右边的代码中。
另外一个匿名函数案例:
(x:Int, y:Double) => x * y

之前的foreach ,实际完整的如下:
v.foreach((n:Double) => println(n))

通常情况下,scala能够看到v中的值类似是Double,因此可以简化如下:
v.foreach((n) => println(n))

如果你只有一个参数,忽略括号
v.foreach(n => println(n))

如果你只有一个参数,还可以将节省参数,用下划线替代:
v.foreach(println(_))

最后,如果函数方法只是调用一个其他方法,而且那个方法只使用一个参数,最后可以成为:
v.foreach(println)

可惜,foreach 有副作用,它不能返回任何值,大部分情况我们需要操作一个collection,并且返回值,然后,在这个返回结果上做一些操作(map/reduce),那么使用map,注意它不是数据结构Map。

v.map(n => n * 2)

将v中每个值乘以2,然后返回结果:
Vector(2.2, 4.4, 6.6, 8.8)

简写如下:
v.map(_ * 2)

还有其他操作方法:


v.reverse
v.sum
v.sorted
v.min
v.max
v.size
v.isEmpty

v.permutations 是产生一个遍历指针iterator
v.permutations.foreach(println).

另外有用功能是zip,可以把两个集合合并在一起:
Vector(1,2,3).zip(Vector(4,5,6))
结果是:
Vector((1,4), (2,5), (3,6))

v.zip(v.map(_ * 2))结果是:
Vector((1.1,2.2), (2.2,4.4), (3.3,6.6), (4.4,8.8))

匿名函数非常简单易用,但是如果你觉得难以理解,那么可以使用标准函数:
def timesTwo(d: Double) = d * 2

v.zip(v.map(_ * 2))就变为:v.zip(v.map(timesTwo))

模式匹配


trait Color
case class Red(saturation: Int) extends Color
case class Green(saturation: Int) extends Color
case class Blue(saturation: Int) extends Color

def matcher(arg:Any): String = arg match {
case "Chowder" => "Make with clams"
case x: Int =>
"An Int with value " + x
case Red(100) =>
"Red sat 100"
case Green(s) =>
"Green sat " + s
case c: Color =>
"Some Color: " + c
case w: Any =>
"Whatever: " + w
case _ =>
"Default, but Any captures all"
}

val v = Vector(1,
"Chowder", Red(100), Green(50), Blue(0), 3.14)
v.foreach(x => println(matcher(x)))

(banq注:在Java中大概要使用visitor模式来实现,复杂些)
match语法类似Java的switch 。但是break不是必须的。

case class对于模式匹配特别有用,因为可以分解它。Any是所有对象的根class,类似Java中的primitive类型,


Actor并发模型
这个在jdon已经讨论过多次,是一种Actor模型,通过异步消息通讯,可异步并发。要比Java多线程模型并发性能更好,彻底杜绝了共享锁。



// Bunnies.scala (Run as script: scala Bunnies.scala)
case object Hop
case object Stop

case class Bunny(id: Int) extends scala.actors.Actor {
this ! Hop
// Constructor code
start()
// ditto
def act() {
loop {
react {
case Hop =>
print(this +
" ")
this ! Hop
Thread.sleep(500)
case Stop =>
println(
"Stopping " + this)
exit()
}
}
}
}

val bunnies = Range(0,10).map(new Bunny(_))
println(
"Press RETURN to quit")
readLine
bunnies.foreach(_ ! Stop)

(banq注:看到这段感觉很象Java的线程类,是不是可以改造一下呢)

act()方法是一个自动的 match方式,loop{ react{看上去怪,但是scala的关键,开始你能使用loop来打开邮箱消息的match状态,它实际是一个协调的多任务,每个任务做点事情然后放弃控制权,让下一个任务,这种方式好处没有共享内存,因此可以拓展伸缩,可以达数百万个任务。

注意class Benny开始两行:
this ! Hop
start()
scala不能在方法内进行对象初始化,放到方法之外,类中,!感叹号是发送消息,然后调用start()开始消息不断循环检查。

以上是Scala主要特征,本人看下来感觉是简单些,特别在了解了函数编程和Actors这两个主要编程范式和思想,我在Jdon其他帖子花过大量时间来讨论,有人感觉Scala复杂,可能是因为开始被误导?实际Scala比较简单强大。


Programming in Scala, First Edition Scala编程第一版在线英文

另外一篇文章Scala的问题
blog:scala_problems [kotek.net]
作者使用了18个月的Scala,发现其问题如下:
1.编译速度, 相当慢,200多个类花费2分钟。
2.增量编译还没有,Eclipse等能够立即对类型等报错。Scala这方面还落后于Java
3.发布的jar变得巨大
4.不再可能返回java
5.二进制不兼容,2.7, 2.8 and 2.9彼此不兼容
6.Bugs and release cycle,作者每4个月总能发现新BUG,没有Java成熟
7.没有紧密的Loop循环,for(i <- 0 until 100){}将创建大量对象。
8.其实不是真正的Actor模型,Scala被业外人士奉为Actors模型的神物,业内却很少谈。 作者认为,Scala只是让他写出更好的Swing代码, 与JDBC更紧密整合, 做更多更好的XML处理, 进行Android编程。
[该贴被banq于2011-07-09 08:04修改过]

语言的纷争永远是无止境,找准自己的那一个就好,我还是蛮喜欢Scala。