编译
  scalac HelloWorld.scala
  scala HelloWorld


CLASSPATH 相关
  通过环境变量或者执行参数
  $SCALA_HOME
  scala -cp


基础文法
  scala 可以不写分号, 但是如果需要断行, 需要注意如
    a LF + b 不行, 必须写成 a + NF b 或者 (a LF + b)

  注释有两种, /* ... */ 和 // ..

  if 语句语法是
    if (COND) EXPR else EXPR

  while 语法
    while (COND) EXPR

  for 语法是
    for (PATTERN <- EXPR) EXPR
  也可以用花括号
    for {PATTERN <- EXPR} EXPR
  可以有 guard
    for (PATTERN <- EXPR if COND if COND...) EXPR
  如
    for (v <- 1 until 20 if v % 2 == 0 if v % 3 == 0) println(v)
  还可以做模式匹配 destructuring
    for (v <- (1 to 20) zip (20 to 1 by -1);
        (x,  y) = v;
        if (x % 2 == 0) && (y % 3 == 0))
      println(x,  y)


import 和 package
  scala 和 package 和 Java 类似, 依赖文件系统, 名称也类似 com.example.xxx
  import 有几种格式
    import users
    import users.User
    import users.{User, UserPref}
    import users._  // 同 Java: import users.*
    import users.{UserPref => UP}  // Python: from users import UserPref as UP
    import _root_.users._  // Rust: use ::users::*
  import 可以在文件中任何地点出现, 局部作用域都可以.
  默认导入
    * scala._ (e.g. Boolean)
    * java.lang._ (e.g. String)
    * scala.Predef._ (e.g. assert/assume/require/ensuring)


声明变量
  基本格式如
    val IMMUTVAR [: TYPE] = INIT_VAL
    var MUTVAR [: TYPE] = INIT_VAL

  如
    val a = if (true) 5 else 6 // 5
    val b = a + 1                      // 6

    val succ = (x: Int) => x + a

    val t: (Int, Int) = (2, 3)  // 声明 Tuple, 函数也可以返回 Tuple
    val t: (Int, Int) = 2 -> 3) // 另外的语法
    t._1                        // 返回第一个元素: 2
    val (v1, v2) = t            // 类似 Rust 的模式匹配解构


声明函数
  基本语法是
    def NAME(ARG: TYPE [= DEFAULT_VAL]): RET_TYPE = EXPR
  其中 RET_TYPE 可以省略. 但是递归函数必须指明返回类型

  如
    def c1 = a + 1 // 完全无参数的函数
    println(c1)    // 调用函数

    def c2() = a + 1 // 无参数的函数
    println(c2)      // 调用函数
    println(c2())    // 也是调用函数

    def fact(x: Int): Int = {
      print(s"calling fact($x)")
      if (x == 0) 1 else x * fact(x-1)
    }

    def sum(x: Int*): Int = (0 /: x)(_+_) // 变长参数
    sum(1, 2, 3, 4)                       // 10
                                          // Int* 事实上是 Seq[Int]

  通常, 如果函数有副作用, 那么定义和调用都该用 c2() 的形式.
  没副作用可以用 c1 的形式.

  调用的时候可以类似 Python 指定传参名, 如
    def foobar(x: String="Hello", y: String="World") = println(s"$x $y")
    foobar()        // Hello World
    foobar("Hi")    // Hi World
    foobar(y="Tom") // Hello Tom

  调用还可以用花括号而非圆括号, 无论是 def 函数还是 val 函数,
  主要用在匿名函数的参数, 如
    def invoke(x: => Unit) = x
    invoke { println("Hello world" }

  函数定义可以嵌套

  函数不会自动 currying, 因此为了 currying 需要写
    def add(x: Int)(y: Int) = x + y
  它和一起定义 x, y 是不同的. 如果希望从这种定义中取得部分函数, 需要一个下划线
    val add2 = add(2)_
  之后
    add2(6)   // 8

  函数类型是 args => ret, 如
    def add(x: Int)(y: Int) = x + y  // add 类型: Int => Int => Int
    def foo(f: Int => Int => Int) = println(f(2)(3))
    foo(add)  // 5
  上面的例子, Java 兼容的写法是 function2[Int, Function2[Int, Int]]
    import scala.Function1
    def bar(f: Function1[Int, Function1[Int, Int]]) = println(f(2)(3))
    bar(add)  // 5

  传参的时候默认是按值传参, 但是也支持按名传参, 就是对传入值做 lazy 计算
  如
    var a = 0
    def foobar0(x: Int) = { a = 2; println(x) }
    foobar0(a)   // 0
    a = 0
    def foobar1(x: =>Int) = { a = 2; println(x) }
    foobar1(a)   // 2


高阶函数
  声明局部函数的格式如
    (x: Int) => x+1
  类型是
    Int => Int

  作为参数时, 可以推断出 x 的类型, 故不用写出类型
    x => x * 2
  也可以简写成
    _ * 2

  scala 和 haskell 不同, 不会自动 curry. 因此
    (x: Int, y: Int) => x + y
  的类型是 (Int, Int) => Int, 而
    (x: Int) => (y: Int) => x + y
  的类型是 Int => Int => Int

  函数类型用在参数和返回值中, 如
    def comp(f: Int=>Int, g: Int=>Int): Int=>Int = ((x: Int) => f(g(x)))


类型
  Any 是所有类型的父类. 其子类包括 AnyVal 和 AnyRef.
  AnyVal 如 Int, Float, Byte, Unit 等.
  AnyRef 如 List, Option, 用户定义的类型等.

  所有类型都是 Nothing 的父类. Nothing 没有实例.
  所有 AnyRef 都是 Null 的父类, Null 只有一个实例 null.

  基础类型有 Int, String, Double, Boolean 等.


声明类
  声明语法是
    class CLASS_NAME(CTOR_ARG: ARG_TYPE [= DEFAULT_VAL]...)
      DECL*
    }
  DECL 包括方法 def, 成员 val 和 var, 类型代称 type, 如
    class Foobar {
      def foobar() = println("foobar")
      private val = 24
      type Output
    }
  构造函数传入的参数可以直接用, 如
    class Circle(r: Double) {
      def area = 3.14 * r * r
    }
    var c = new Circle(5.0)
    c.area

  如果希望将传入的参数保存到一个域, 可以直接在参数列表中声明
    class Foobar(a: Int,  val b: Int, var c: Int, private var d: Int)
    var c = new Foobar(1, 2, 3, 4)
    println(c.a)  // Error: no field a in c
    println(c.b)  // 2
    println(c.c)  // 3
    println(c.d)  // Error: field d not accessible
    c.b = 6       // Error: reassignment to val
    c.c = 7       // ok

  实例化语法如
    new CLASS_NAME(ARGS)
  可以把 new 完的结果赋给 val 或者 var

  调用方法语法如 INSTANCE.METHOD(ARGS),
  但可以省略变成 INSTANCE METHOD(ARGS)
  如 (1::2::3::Nil) foreach(println)
  这相当于把 method 看成 operator.
  如果没有 ARGS, 普通情况下不能直接 INSTANCE METHOD, 需要
    import scala.language.postfixOps

  scala 中 method 名可以是特殊字符,
  所以 2 + 3 实际上是 2.+(3), 3 * 5 实际上是 3.*(5)
  这也就是算符重载的方法: def +(other: ...) ...
  算符重载的优先级是 */% 大于 +- 大于 : 大于 =! 大于 ... 大于 普通标识符

  可以有 getter 和 setter, 如
    private var _NAME: TYPE
    def NAME = _NAME
    def NAME_=(ARG: TYPE): Unit = { // assignment }

  和 Java 一样, scala 允许嵌套类, 但是创建不能像 Java 一样 new Outer.Inner
    class Outer {
      class Inner(val i: Int)
      def newInner(i: Int) = new Inner(i)
    }
    val a = new Outer
    val b = new Outer.Inner(5) // Error
    val b = a.newInner(5)  // Ok. b: Outer#Inner


case class
  和普通的类不同, 类似数据类, 可以用来做模式匹配.
  所有域都是 public 的 val. 所以默认 immutable, 并且按值比较.
  声明语法如
    case class CLASS_NAME(ARG: ARG_TYPE, ARG: ARG_TYPE...)
  如
    case class Person(name: String, age: Int, sex: String="male")

  实例化不需要 new (原理是 case class 的 apply 函数负责对象构建)
    CLASS_NAME(ARGS)
  如
    var jack = Person("Jack", 23)
    var tom = Person(name="Tom", age=23)

  可以复制, 如
    var jackie = jack.copy(sex="female")


模式匹配
  模式匹配的基本格式是
    EXPR match {
      ARM*
    }
  其中 ARM 的结构时
    case PATTERN GUARD? => EXPR
  如
    val x = 23
    x match {
      case 23 => "twenty-tri"
      case 20 => "twenty"
      case _ => "others"
    }

  模式匹配必须是完备的, 否则会产生 *运行时错误*
    var x = 5
    x match {
      case 23 => "twenty-tri"
      case 20 => "twenty"
    }

  一个常见的设计方法是, 实现类似 Rust 的 enum 类型可以使用抽象类加上 case class,
  当然 trait 也可以
    abstract class Event
    case class Move(x: Int=0, y: Int=0) extends Event
    case class Quit() extends Event
    case class Talk(s: String) extends Event

  之后
    def tellEvent1(e: Event) = {
      e match {
        case m: Move => println("Moving " + m)
        case _: Quit => println("Quitting")
        case _: Talk => println("Jabbering")
      }
    }

  并且可以在 PATTERN 中对被 match 的 EXPR 做 destructuring, 如
    def tellEvent2(e: Event) = {
      e match {
        case Move(x, y) => println(s"Moving with dx=$x, dy=$y")
        case Quit() => println("Quitting")
        case Talk(s) => println(s"He's talking like $s")
      }
    }

  Pattern 也可以有 Guard
    def tellEvent3(e: Event) = {
      e match {
        case Move(x, y) if x > 5 || y > 6 => println("Moving too fast")
        case Talk(s) if s.length > 10 => println("Stop jabbering man")
      }
    }

  上面的设计模式可以把抽象类或者 trait 加上 sealed, 要求所有子类都在同一文件中
  这样就完全防止外部拓展的可能, 便于编译器检查
    sealed trait Event
    case class Move(x: Int=0, y: Int=0) extends Event
    case class Quit() extends Event
    case class Talk(s: String) extends Event

    def tellEvent4(e: Event) = {
      e match {
        case Move(x, y) if x > 5 || y > 6 => println("Moving too fast")
        case Talk(s) if s.length > 10 => println("Stop jabbering man")
      }
    }


object
  相当于 singleton, 它们的类只有一个实例.
  声明如
    object OBJ_NAME {
      DECL*
    }
  如
    object Logger {
      def log(a: String) = println(a)
    }
    Logger.log("Hello world")

  如果 OBJ_NAME 和某个类的名称相同, 称这个对象是该类的 companion object.
  companion 对象和类必须在同一文件中定义
  通常的用途有
    * 将 Java 的 static 方法放到 companion object 中.
      这是不能通过 companion 类的对象来调用 static 方法.
    * 通常把工厂方法放到 companion 对象中
  如
    object Cat {
      def meow() = println("Meow~~")
      def fromNothing() = Cat("God")
    }
    case class Cat(val name: String) {
      import Cat._
      def hello() = { println(s"Hello I'm a cat $name"); meow }
    }
    Cat.fromNothing().hello()


trait
  包含域和方法的类型. 多个 trait 可以结合.
  声明如
    trait TRAIT_NAME {
      DECL*
    }
  DECL 中域和方法可以有值或实现, 它们是默认值和默认实现

  方法实现 trait 如
    class CLASS_NAME(ARGS) [extends {SUPER|TRAIT} [with TRAIT*]]
  其中如果重写默认实现或默认值, 需要用 override
    override DECL

  有一个概念就是 mixin.
  通过一系列的 mixin traits, 可以直接构建一个类.
  因为 trait 有数据和方法, 又有默认值和实现, 所以它克服了 interface 的缺点.
  又因为 trait 更多地是语义的概念, 和域的内存排布没有直接联系,
  所以克服了 (C++ 式) 多重继承的缺点.

  为了表达一个对象需要有多个 trait, 可以用 compound type
    trait Hello {
      def hello(): Unit
    }
    trait Bye {
      def bye(): Unit
    }
    def helloAndBye(v: Hello with Bye) = { v.hello; v.bye }

  如果希望表达, 要用 trait A 的时候必须以形式 A with B 出现,
  但是不想让 A 继承 B, 那么应该使用 self type, 形式是一个声明 this: B => 如
    trait Sentence {
      val str: String
    }
    trait Writer {
      this: Sentence =>
      def write() = { println(s"writing $str") }
    }


泛型
  类型参数传统是用 A 代表
  泛型类如下, 泛型参数在类名后
    abstract class Set[A] {
      def contains(a: A): Boolean;
      def add(a: A): Unit
    }
  泛型函数如下, 类型参数在函数名后
    def externalAdd[A](xs: Set[A], x: A): A = { ... }
  使用泛型的时候通常可以让编译器推断类型参数

  scala 默认泛型也是 invariant 的, 也就是说 (is 表示 subclassof)
    A is B 不能推出 T[A] is T[B], 也不能推出 T[B] is T[A]
  不能把 List[Char] 当成 List[Int] 来用, 不然就可能向 List[Char] 中加入 Int 了

  如果 A is B 可以推出 T[A] is T[B], 则称 T 的泛型是 covariant 的.
  上面提到的 covariant 问题只有当允许向 List[Char] 中加入元素,
  i.e. List 为 mutable 的时候才会出现
  因此默认的 immutable.List 是满足 List[Char] is List[Int] 性质的.
  为了让泛型变成 covariant, 只需在类型参数前缀 +, 如
    SEALED abstract class List[+A]  // scala.collection.immutable
  或者更易理解的,
    Cat is Animal
    ->  Factory[Cat] is Factory[Animal]

  相反, 若 A is B 可以推出 T[B] is T[A], 那么称 T 的泛型是 contravarint 的.
  给类型参数加前缀 - 来让泛型变成 contravarint 的, 如
    abstract class Printer[-A] {
      def print(v: A): Unit
    }
  或者更易理解的,
    BinaryAST is AST
    ->  Visitor[AST] is Visitor[BinaryAST]

  covariant 和 contravariant 的例子还有一个就是,
  函数 T -> R 实际上是 Function1[-T, +R], i.e. (super T -> sub R) is (T -> R)
  因为 (super T -> sub R) 也完成了 (T -> R) 的工作

  泛型类型也可以加上限制, 如要求类型参数 T 是实际类型 A 的子类,
  A 称为类型参数 T 的上界. 记为 T <: A 如
    def Animal2List[A <: Animal](a: A): List[A] = List(a)
    Animal2List(Cat("kitty"))   // List[Cat] = List(Cat("kitty"))

  要求 T 必须是 A 的父类, 则 A 是 T 的下界, 记为 T >: A

  从形式化类型论的角度很容易区分 variance 和 bounding.

  在 trait 和抽象类中, 允许有抽象类型如
    trait Wrapper {
      type T
      val elem: T
    }
  允许抽象类型使用泛型操作如上下界
    trait SeqWrapper extends Wrapper {
      type U
      type T <: Seq[U]
      def length = elem.length
    }
  有时可以把 type parameter 变成抽象类型


for comprehension
  comprehension 就类似 python 中的 [x*x for x in range(20)]
  scala 基本的 comprehension 的格式如
    for (ENUMERATORS) yield EXPR
  comprehension 生成一个列表, 并非 iter. 如
    for (x <- 1 to 10; if x % 3 == 0) yield x*x   // Vector(9, 36, 81)

  ENUMERATORS 可以包含以下元素, 分号隔开
    * 局部 scope 的新变量, 如 x <- 1 to 10
    * guards: 如 if x + y == 10
  如
    var pairs = for (i <- 1 to 10; j <- 1 to 10; if i + j == 10) yield (i, j)

  如果不写 yield, 那么 for 的结果类型是 Unit, 通常这用来模拟普通语言的 for 循环
  如
    for (i <- 1 to 10) println(i)


implicit
  函数可以有 implicit 参数. 这种参数不用显式传入, 而是 scala 去寻找然后自动传入
  寻找的规则是
    * 首先检查, 函数 call site 处所有用 implicit 前缀定义的符号
    * 然后去 implicit 参数的类型的 companion object 中寻找
  如
    implicit val a = 5;
    def foobar(implicit v: Int) = { println(v) }
    foobar  // 5
    foobar(6)  // 6
  当然除了函数参数, 构造函数参数也可以是 implicit 的

  implicit 必须和 currying 结合, 就是说不能
    def foobar(x: Int, implicit y: Int)
  而只能写
    def foobar(x: Int)(implicit y: Int)
  并且如果写成
    def foobar(implicit x: Int, y: Int)
  那么 x, y 都是 implicit 的

  scala 可以支持隐式类型转换, 不过隐式类型转换经常导致问题所以需要手动打开
    import scala.language.implicitConversions
  从 S 到 T 的隐式类型转换是一个 implicit S => T 的函数
  然后会在两种情况下执行隐式转换
    * 在期望 T 类型的时候却看到一个 S 类型:
      寻找 S => T
    * e 是 S 类型, 但是 S 类型没有 m 字段, 但却是用了 m.e:
      寻找 S => T, 而且 T 有 m 字段
  如
    implicit def double2int(x: Double): Int = x.intValue
    def printInt(x: Int) = { println(s"printInt $x") }
    printInt(3.5)


apply 和 unapply
  类和对象可以实现 apply 方法, 它们的语义也很简单
    class Foobar { def apply(s: String) = println(s"Hello $s") }
    var t = new Foobar
    t("Hi")  // Hello Hi

  而 unapply 主要是用来实现模式匹配的 destructuring,
  相当于从 apply 的结果中抽取参数.
  通常 unapply 的参数是 apply 的返回类型,
  unapply 的返回类型是
    * Bool: 看这个返回值是否是 apply 产生的
    * Option[T], Option[(T1, T2...)]: 返回应用 apply 的参数

  注意 case class 自动定义了 unapply, 所以如果想做自定义的 unapply, 不能用 case class.


常见操作
  range 操作
    val inclusive_range = 1 to 10    // Range.Inclusive(1, 2, 3, ..., 10)
    val exclusive_range = 1 until 10 // Range(1, 2, 3, ..., 9)

  批量赋值
    val (x, y) = (5, 6)
    println(s"x is $x and y is $y")  // x is 5 and y is 6

  随机数
    import scala.util.Random
    val x = Random.nextInt(10)

  防御性编程
    def doublePositive(n: Int): Int = {
      require(n > 0)
      n * 2 // logic
    } ensuring(n => n >= 0 && n % 2 == 0)


Option
  scala.Option[+A]
    最合适的是把 Option 当成 collection 然后用 monad 来做
      val name: Option[String] = request getParameter "name"
      val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
    也可以用模式匹配来做
      name match {
        None => println("Error")
        Some(name) => println(name)
      }
    函数包括
      getOrElse(v: A) -> A


容器
  容器都在 scala.collection.immutable 和 scala.collection.mutable 中.
  最常用的 List 就是 immutable.List.

  容器的构造语法都统一的是
    List(1, 2, 3), Seq(6, 7, 8), HashMap(1 -> "one", 2 -> "two")
  scala 有一个容器继承树, 最根上是 Traversable, 然后是 Iterable

  Traversable[+A] 提供的函数有如下
    foreach
      var a = List(1, 2, 3, 4, 5)
      var b = Vector("a", "b", "c")
      a foreach print   // 12345
      b foreach print   // abc
    ++
    ++:
      a ++ b   // List[Any](1, 2 ...)
      a ++: b  // Vector[Any](1, 2 ...)
    foldLeft
    foldRight
    /:
    :\
    reduceLeft
    reduceRight
      (a foldLeft 20)(_-_)  // 5
      (a foldRight 20)(_-_) // 5
      (5 /: a)(_+_)         // 20
      (a :\ 5)(_+_)         // 20
      a reduceLeft (_-_)    // -13
      a reduceRight (_-_)   // 3
    sum
    product
    min
    max
      println(a.sum, a.product, a.min, a.max)   // (15, 120, 1, 5)
    tail
    init
    take
    drop
    head
    last
    find
    slice
      a.tail              // List(2, 3, 4, 5)
      a.init              // List(1, 2, 3, 4)
      a take 3            // List(1, 2, 3)
      a drop 3            // List(4, 5)
      a.head              // 1
      a.last              // 5
      a find (_ % 3 == 2) // Some(2)
      a slice(2, 4)       // List(2, 3)
    filter
    map
    flatmap
      a filter {x: Int => (x & 1) == 1}     // List(1, 3, 5)
      a map (_+1)                           // List(2, 3, 4, 5, 6)
      a map {x: Int => List.fill(x)(x)}     // List(List(1), List(2, 2), List(3, 3, 3) ...)
      a flatMap {x: Int => List.fill(x)(x)} // List(1, 2, 2, 3, 3, 3 ...)
    isEmpty
    nonEmpty
    size
      a.isEmpty  // false
      a.nonEmpty // false
      a.size     // 5
    exists
    forall
    count
      a exists (_ % 2 == 2) // false
      a forall (_ % 1 == 0) // true
      a count (_ % 2 == 1)  // 3
    splitAt
    partition
    groupBy
      a splitAt 2              // (List(1, 2), List(3, 4, 5))
      a partition (_ % 2 == 1) // (List(1, 3, 5), List(2, 4))
      a groupBy (_ % 3)        // Map(2 -> List(2, 5), 1 -> List(1, 4), 0 -> List(3))