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,等效。
沒有留言:
張貼留言