Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

ScalaのAdvanced Exampleを写経する(4)-lazy evaluation

いわゆる遅延評価ってやつ。

package sample.snippet

object LazyLib {
  
  /** Data type of suspended computations. */
  abstract class Susp[+A] extends Function0[A]

  class SuspImpl[A](lazyValue: => A) extends Susp[A] {
    private var maybeValue: Option[A] = None
    
    override def apply() = { 
      println("apply() is called.")
      maybeValue match {
      
	      case None =>
	        val value = lazyValue
	        maybeValue = Some(value)
	        value
	      case Some(value) =>
	        value
      }
    }
    
    override def toString() = maybeValue match {
      case None => "Susp(?)"
      case Some(value) => "Susp(" + value + ")"
    }
  }
  
  /** Delay the evaluation of an expression until it is needed. */
  def delay[A](value: => A): Susp[A] = new SuspImpl[A](value)

  /** Get the value of the delayed expression. */
  implicit def force[A](s: Susp[A]): A = s()
}

object LazyEvaluation {
  import LazyLib._
  
  def main(args: Array[String]) = {
    val s: Susp[Int] = delay { println("evaluating..."); 3 }
    println("s = " + s) // show that s is unevaluated
    println("s() = " + s()) // evaluate a
    println("s = " + s) // show that the value is saved
    println("2 + s = " + (2 + s)) // implicit call to force()
    
    println
    println    
    
    val s1 = delay { Some(3) }
    val s11: Susp[Some[Int]] = s1
    val s12: Susp[Option[Int]] = s11 // the type is covariant
    
    println("s12 = " + s12)
    println("s12() = " + s12())
    println("s12 = " + s12)
    
  }  
  
}
val s12: Susp[Option[Int]] = s11 // the type is covariant

「covariant」は共変って意味。
要はs11をSusp[Some[Int]]で定義してるから、SomeのスーパークラスであるOption型でも定義できるということ。
Someは必ずOptionでもあるので。


実行結果。

s = Susp(?)
apply() is called.
evaluating...
s() = 3
s = Susp(3)
apply() is called.
2 + s = 5


s12 = Susp(?)
apply() is called.
s12() = Some(3)
s12 = Susp(Some(3))

つまりdelayがたの変数「s」とか「s1」は、関数を呼び出さない限り値は初期化されていない。
だからs()を呼び出す前にsをtoString()すると「Susp(?)」だし、同様にs12も「Susp(?)」。
s()を呼び出すとapply()が勝手に呼び出されて値が評価される。
ちなみにapply()はFunction0クラスの抽象メソッド。
Function0の0は0パラメータつまりパラメータなしってことみたい。