code

2016年11月19日 星期六

Scala筆記 29 - stateful objects

State in Scala

scala不是純粹的functional language,他可以有state。所謂的state就是一個變數值它會隨著執行的歷史而改變。此時substitution model就不能適用,因為一個變數值在每個時間點可能不一樣。如果要宣告一個可以改變的變數,用var而非val keyword來宣告。

如果一個object的狀態會隨著history改變的話,就是一個stateful object。


在Stream中用mutable variable實作lazy val

之前在筆記25我們說Stream可以是以下的implementation:

def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
  def head = hd
  lazy val tail = tl
  ...
}

我們宣告tail為lazy val(compiler會儲存evaluation,免得stream每次要用到tail就要重新evaluate tail一次(原本的non lazy-val版本如下):

def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
    def isEmpty = false    
    def head = hd
    def tail = tl
  }

我們可以把lazy val的tail改成var來宣告:

def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
  override def head = hd
  private var tlOpt: Option[Stream[T]] = None
  override def tail: Stream[T] = tlOpt match {
    case Some(x) => x
    case None => tlOpt = Some(tl); tail
  }
}

可以看到tail method是一個option,如果已經有值了(case Some(x)),那就回傳那個tail Stream,否則就去evaluate tl參數,將mutable variable tlOpt重新assign Some(tl),並且回傳重新呼叫自己tail method,此時不會進入recursion,因為tlOpt的match type已經改變成Some(x)了。

所以在以上例子可以看到,mutable variable可以用來實作lazy val。


stateful objects的等值與等效

我們之前非常容易判斷兩個value是不是等值,只要看其定義的expression是否一樣,所以可以用substitution model來重寫expression。但是在stateful objects就無法了,因為每個object的state不一樣的話,即便定義一樣,也不能說兩個instance是同一個。

但是兩個不同的instances,如果所有可能的不同順序的operations都能產生一樣的效果,可以說他們是operational equivalence,等效。


沒有留言:

張貼留言