minimize

事業拡大のため、新しい仲間を募集しています。
→詳しくはこちら

call-by-name

次のコードを見てみよう。

class Drop
{
  var empty : Boolean = true
  
  def func() : Unit = {
    await (empty == true)
  }
  
  private def await(cond: => Boolean) =
    while (!cond) { lock.wait() }
}

func 内で、await 関数に empty を引数として渡している。
await は 引数で受け取った cond を while 文の条件にしてループしている。

ここで鋭い人は「cond はローカル変数だから、この while 文は永久ループに陥るのでは?」と思うかもしれない。
しかし、もっと鋭い人は以下のことに気付いただろうか?
そう、cond の型は Boolean ではない。「Boolean 型を返す関数」なのだ。
つまり !cond は実際には関数呼び出しとなり

while (!(empty == true)) { lock.wait() }

と同等の処理をすることになる。

こういったことを Scala では「call-by-name」と呼ぶ。
value を渡すのではなく、name(言い換えれば関数、すなわちコードブロック)を渡すという意味だ。
このパターンを使うと、コードを関数の内部で実行することが可能となる。
これを利用すると「コードの実行を必要なときまで遅らせる」ことも出来る。

ちなみに、値渡しにしたいときには await の定義を

private def await(cond: Boolean)

とする。

逆に言えば、たったこれだけの記述で Scala では call-by-name を使うことができる。
これの素晴らしいところは、関数を呼び出す側がその違いを全く意識しなくても良いということ。
call-by-value でも call-by-name でも、呼び出し側は同じ記述で call できる。

最後にこれらを整理しておこう。

値渡し

def tfunc(cond: Boolean) { println(cond) }
tfunc( empty == false ) // 値渡し。式はその場で評価される

call-by-name

def tfunc(cond: => Boolean) { println(cond) }
tfunc( empty == false ) // 名前渡し。式は関数内部で評価される

関数

おまけとして。

def tfunc(cond: () => Boolean) { println(cond) }
tfunc( empty == false ) // コンパイルエラー
tfunc( () => empty == false ) // これならOK

このような記述法では、cond は引数無の関数を要求する。