Scalaの名前渡しについて個人的にしっくりきた説明

# Scalaの名前渡しについて個人的にしっくりきた説明

 ということで、Scalaの名前渡しについて調べていて個人的にしっくりきた説明があったので、まとめてみようと思います。

###参考記事
- [Scalaの名前渡しは遅延評価ではない](http://kannokanno.hatenablog.com/entry/20130202/1359777436)
- [名前渡し Call-by-name にて引数のコード実行タイミングを制御する](https://blog1.mammb.com/entry/20100421/1271865690)

# Scalaの名前渡しについて
　初めにScalaの名前渡し(call-by-name)ってなんぞ？ってところから説明します。それは知ってるって人は次の項目にskip
Scalaの名前渡しは公式のドキュメントではこう書いてあります
>By-name parameters are evaluated every time they are used. They won’t be evaluated at all if they are unused. 

簡単に要約すると
>名前渡しの引数はその引数が使われるときに評価される。その引数が使われなければ評価されない。

です。
ほう。
これをきちんと理解するには、まず普通の関数に引数を渡したときにどのような挙動をしているかを知る必要があります。
普通に掛け算を行う関数を作りました。

```Scala:normal_example.scala
object workSpace {

  var f = 1
  var s = 2

  def main(args: Array[String]): Unit = {
    val res = add(f, s)
    println(res) // 2
  }

  def mul(first: Int, second: Int): Int = {
    first * second
  }
}

```
(Scalaではvarは使うべきでないですが、説明のためご了承を、、）
結果は当然 `1*2` で2です。では、こうするとどうなるでしょうか。

```Scala:normal_example2.scala
object workSpace {

  var f = 1
  var s = 2

  def main(args: Array[String]): Unit = {
    val res = add(f, s)
    println(res) // 2
  }

  def mul(first: Int, second: Int): Int = {
    f = 2
    first * second
  }
}

```
`first * second` が呼ばれる前にfの値が2になっているから `2*2` で結果は4でしょうか？
結果は `3` です。
これは引数のfirstの値が決まるのが `mul関数の呼び出し時`のためです。
なので、結果は３だったというわけです。つまりは値渡しということ。
ここで名前渡しの登場です。関数を次のように書き換えます。

```Scala:call_by_name_example.scala
object workSpace {

  var f = 1
  var s = 2

  def main(args: Array[String]): Unit = {
    val res = add(f, s)
    println(res) // 4
  }

  def mul(first: => Int, second:Int): Int = {
    f = 2
    first * second
  }
}
```

`first: Int` が `first: => Int` に変わりました。これで結果は `4` になります。
これでfirstの値の評価が `mul関数の呼び出し時` から `firstが使われる時` に変わったということです。なので `first * second` でfirstが使われる時の値、つまり `f = 2` をした後の値が使われるわけです。
これで初めの定義の意味は分かってもらえたでしょうか？？
皆さんも疑問に思ったかも知れませんが、僕はこれをみたときにこれは参照渡しとはどう違うのか、と疑問に思いました。
また、遅延評価とは違うのか？と疑問に思った人もいるかも知れません。

# 参照渡しとはどう違うのか
まず、参照渡しとの違いです。これは、次の遅延評価との違いにも繋がります。
上の例をみると、僕はfの参照を渡せば行けそうだな、と思いました。
と思ったのですが、そもそもScalaには参照渡しは存在していなかったので、参照渡しなどはできませんでした。（勉強不足でした）
ですが、Scalaという枠を取っ払えば考えられます。
定義的にはこのような違いがあるようです。
>名前渡しでは値でも参照でもなく、式がそのまま渡される。

つまり参照渡しでは当然参照が渡される。名前渡しでは式そのままが渡されるということでした。
イメージがつきにくいですね。
参考サイトの二つ目の次の例がわかりやすかったです。
まず、名前渡しを使わなかった例です。

```Scala:example3.scala

def hello() = {
  println("in hello()")
  "HELLO"
}

def print(s : String) = { 
  println("print() start")
  println(s)
  println("print() end")
}

def greeting() = print(hello())
```

この結果は以下のようです。

```result
in hello()
print() start
HELLO
print() end
```

名前渡しではないので、print関数の呼び出し時にhello()が評価されるので、まず `in hello()` の出力がされます。
これをこのように書き換えます。

```Scala:example4.scala
object workSpace {
  def main(args: Array[String]): Unit = {
    greeting()
  }
  def hello() = {
    println("in hello()")
    "HELLO"
  }

  def print(s : => String) = {
    println("print() start")
    println(s)
    println("print() end")
  }

  def greeting() = print(hello())
}
```

結果は以下

```result
print() start
in hello()
HELLO
print() end
```

sを使うときににhello()が評価されるのでこのようになります。ここの理解としては、関数hello()が呼び出されるときに評価される、でもいいのですが、hello()関数がprint関数内の `s` の部分ににコードブロックとして埋め込まれると理解するとわかりやすくなります。
つまりこんな感じです。

```Scala:example5.scala
object workSpace {
  def main(args: Array[String]): Unit = {
    print()
  }

  def print() = {
    println("print() start")
    println({println("in hello()"); "HELLO"})
    println("print() end")
  }
}
```
こう考えると、名前渡しの定義の `式がそのまま渡される` ということも理解しやすくなるのではと思います。

#遅延評価との違い
　これも、先ほどのように `コードブロックとして埋め込まれる` と理解すると説明ができます。
まず、遅延評価とは次のように考えます。

>一旦計算された値はキャッシュをすることが可能であり、遅延プロミスは最大で一度しか計算されないようにすることができる
[遅延評価 - Wikipedia](https://ja.wikipedia.org/wiki/%E9%81%85%E5%BB%B6%E8%A9%95%E4%BE%A1)

つまり、計算結果がキャッシュされて同じ計算が呼ばれるとキャッシュを使用するというのが遅延評価です。
ここまで来ればもうわかると思いますが、名前渡しの場合コードブロックが埋め込まれるという説明であれば、キャッシュを保持しているわけではなく、呼び出されるたびに演算を行っているため、遅延評価とは異なるということがわかると思います。

# 最後に
 今回はScalaの名前渡しについて自分なりに調べた結果、わからなかった部分の説明をしてみました。まだまだScalaは初心者で間違っているところもあるかも知れないので、もしご指摘があればコメントなどにお願いします。
Scala難しいですね。。