scala 初步

听说 scala 比较长时间但不知道是什么,最近头疼 pig 不好用找到了 cascade,而据一个同事说 twitter 使用了一个 scalding,是 cascade 上 scala 的 API。其实也一直很好奇,当时 Yahoo! 开源了 hadoop 衍生出来不少有名的项目(比如前面的 cascade 也是 hadoop 上的一个 thin layer),后来开源了 S4 却不见有人用,倒是 twitter 的 storm 似乎备受关注。不过从这两个例子来看,美国很多公司能走到今天这一步,多多少少还是依赖了里面这些 engineering excellency。国内这方面还非常落后,基本上停留在拿来主义和山寨主义的层次上。

那么先从一些基本的 scala 开始,后面也仔细读读 cascade 的文档。基本的第一自然是流程性的“Hello World”。

object HelloWorld {
  def main (args: Array [String]) {
    println ("hello world!")
  }
}

注意我们可以将这个文件保存为 hello.scala 而不像 java 对文件名有严格的要求,即必须保证类名和文件名一致。但是编译后仍然会产生 HelloWorld.class 和 HellowWorld$.class。

$ scalac hello.scala

object 是 scala 表示 singleton 的方式。执行的方式是

$ scala HelloWorld
Hello World!

但是我们也可以直接使用 java 自己的方式,比如我使用 macports 安装的 scala 就可以如下执行

java -cp /opt/local/share/scala-2.9/lib/scala-library.jar:. HelloWorld

这说明了两个很重要的问题:

  • java 和 scala 是 JVM-compatible 的(就像 native code 一般是 ABI-compatible 的时候可以互相链接调用)
  • scala 也应该提供了一种可能,让我们可以在其提供的 functional language 语法上直接 leverage 大量优良设计的 java 代码。

下面的例子说明了这一点

import java.util.{Date, Locale}
import java.text.DateFormat
import java.text.DateFormat._

object FrenchDate {
  def main (args : Array [String]) {
    val now = new Date
    val df = getDateInstance (LONG, Locale.FRANCE)
    println (df format now)
  }
}

注意这里 Java 的类拥有“类方法/成员”,也有“成员方法/成员”,这个就是 static 关键字导致的区别,在 scala 里面使用两个“东西”分别表示,比如这里 DateFormat 类,getDateInstance 明显是 static 方法,是通过 DateFormat._ 这个 singleton 代入的,而 df 调用的 format 方法是成员方法,比起 java 的调用,这里多了一种选择。

scala 里面所有的东西都是对象,这比起 java 里面留下一些 int、double 之类的东西更干净,更进一步,连函数都是对象,这使得我们可以简单的传递函数;另外提供了 lambda (匿名函数)方便我们书写一些简单的函数。

object TimerAnonymous {
  def oncePerSecond (callback : () => Unit) {
    while (true) {
      callback () ;
      Thread sleep 1000
    }
  }

  def main (args : Array[String]) {
    oncePerSecond (
      () => println ("how time flies...")
    )
  }
}

这里函数的类型是“() => Unit”,意思是 signature 为 (),而返回为 Unit(即 void)。这里我们还使用了 java.lang.Thread 的方法,很明显在 java.lang 中的类我们似乎不需要 import 就可以直接使用。

与 Java 类似的是 scala 自己的继承方式,它本是一个单根继承(root 为 scala.Any),但是后面分为了两个分支,一个是 scala.AnyVal,这主要是一些数值方面的类型(如 scala.Double 等,这部分主要通过 view 产生相互关系,即他们都直接继承了 AnyVal,相互之间只存在 view 的转换,即 implicit conversion,可使用 aInstanceOf 转换)。另一个分支是 scala.AnyRef,这等价是 java.lang.Object(即 Java 的根)。下面是一个简单的 scala 的类。

class Complex (real : Double, imaginary : Double) {
  def re = real
  def im = imaginary

  override def toString () =
    "" + re + (if (im < 0) "" else "+") + im + "i"
}

object ComplexNumber {
  def main (args : Array [String]) {
    val c = new Complex (1.2, 3.4)
    println ("the complex is " + c toString)
  }
}

这个例子中的 toString 是我们覆盖了父类的(即 java.lang.Object)方法,而这个方法在与字符串作用的时候会被自动的调用。

另一个重要的功能是 match,这里有几个基本的用法,下面是一个高级一些的例子,这得到了一个 compile-time 的 AST,可以为 scala 本身设计需要的 DSL,这个作用就像 boost.proto 提供的 template expression 一样。

abstract class Tree
case class Sum (l : Tree, r : Tree) extends Tree
case class Var (n : String) extends Tree
case class Con (v : Int) extends Tree

object TreeExample {
  type Environment = String => Int

  def eval (t : Tree, env : Environment) : Int = t match {
    case Sum (l, r) => eval (l, env) + eval (r, env)
    case Var (n)    => env (n)
    case Con (v)    => v
  }

  def derive (t : Tree, v : String) : Tree = t match {
    case Sum (l, r)          => Sum (derive (l, v), derive (r, v))
    case Var (n) if (v == n) => Con (1)
    case _                   => Con (0)
  }

  def main (args : Array [String]) {
    val exp : Tree = Sum (Sum (Var ("x"), Var ("x")), Sum (Con (7), Var ("y")))
    val env : Environment = {case "x" => 5 case "y" => 7}
    println ("Expression: " + exp)
    println ("Eval with x=5, y=7: " + eval (exp, env))
    println ("Derivative w.r.t. x: " + derive (exp, "x"))
    println ("Derivative w.r.t. x= " + eval (derive (exp, "x"), env))
    println ("Derivative w.r.t. y: " + derive (exp, "y"))
    println ("Derivative w.r.t. y= " + eval (derive (exp, "y"), env))
  }
}

和 java 的继承类似,scala 可以使用 extends 关键字实现单继承;另外为了具有“多继承”的特性,Java 提供了 interface 关键字,但是规定其中不能有实现,这等价于 C++ 的 pure virtual 类且并不含有 code,这定义了一种“对象通信的 protocol”,比如要跟容器打交道很可能提供一个 iterable 的接口,要求对方只能通过 iterator 访问。scala 类似的概念是 traits,但是允许写 code,这跟 C++ 类似(允许 virtual function 带有实现)。另外 scala 也支持类似的 generics,但是用中括号,前面的 Array[String] 就是一种 generics。

——————-
And Abraham was an hundred years old, when his son Isaac was born to him.

Advertisements
scala 初步

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s