Lazy evaluation, Currying を JavaScript で解説してみた（遅延評価、カリー化）

University of the People という無料の大学に行ってます。そこで書いたdiscussionを自分用に保存しておきたいので、ここに記録します。もとは英語で書いてて deepL するだけなので、日本語が変でもすみません。どうしようもない単語だけ英語に戻してます。

lazy evaluation = 遅延評価
higher order functions = 高階関数？
currying = カリー化
partial applicaiton = 部分適用

だと思ってますが、何分学生でこれを初めて勉強してる状態で、学生が課題で書いた内容ですので、間違ってるところは多々あるという前提でお読みください

なお、記事全体は [Tsoding さんのyoutubeそのまんま](https://www.youtube.com/watch?v=E5yAoMaVCp0&feature=emb_title) なので、そっち見たほうが早いです、が、動画が1時間あるので、要約として見てもらえれば的なアレです。



---

# 質問1：レイジー評価について - Lazy evaluation

 まず、Lazy評価なしで問題に直面した場合について、Tsodingの例を使って説明します。ここでは、無限ループのために常にハングアップしてしまうjavascriptの関数があります。

```js
function hang() {
     hang()
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/ac389074-0d40-b89f-251c-e7b14135a439.png)

 また、最初の引数だけを返す関数があります。

```js
function takeFirst(a, b) {
    return a
}
```
 関数takeFirst()に渡される前に第2引数が評価されるため、`takeFirst(true, hang())` を呼び出すとJavascriptがハングアップしてしまうのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/a9c252c9-06c3-fe78-516d-77713a1c1f56.png)



コンパイラは第2引数のhang()を実行する必要がないと思いませんか？第2引数はtakeFirst()の中でさえ使われていないのです。Haskellは同じコードを何のエラーもなく実行します。なぜHaskellはそれができるのか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/26b8af86-e56b-defd-77f5-2171627e1733.png)



Haskellの秘密はLazy評価です。遅延評価は、値が必要になるまで式の評価を保留する戦略です(Tutorialspoint, n.d.)。Javascriptで関数を引数として使用する際にも、同じことができます。次の画像の3つの呼び出しは同じ意味で、第2引数としてまだ実行されていない関数を渡しているだけです。関数化は、言い換えれば実行を遅らせることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/3a7a92cc-5b01-79d1-971a-5996f7ce65fa.png)


次のイメージでjavascriptの実装を見ると、Haskellが`lazuSum()`の内部で何をしているのか理解できるかもしれません。遅延評価は、引数を関数として受け取り、必要なときに関数の内部で実行することで実現されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/abe427ea-51de-569b-d6ca-b5f06e8d9054.png)


Lazy評価の最大の利点は、無駄な実行を減らすことによるパフォーマンスの向上です。上の画像でsum()とlazySum()を使って次のお題を説明します。



# 問題2-1：高階関数(HOF) - Higher order functions(HOF)

 "高次関数とは、関数を引数にとる関数、または関数を返す関数のことです。"(Eliott, 2017) 一般的なHOFは、map()、filter()、zipWidth()などです。したがって、私のsum()はHOFではなく、lazySum()は関数を引数に取るのでHOFとなります。次の段落の別の例 sumOneArg() は、関数を返すので HOF です。

# 質問2-2:  Currying - カリー化

 currying を理解するためには、Haskell の不思議な性質を受け入れなければなりません。つまり、Haskellにおける関数とは、実際には引数を1つだけ取る単項関数 - unary function なのです。えっ、Haskellでは複数の引数を取る関数を宣言できるんだ、と言われるかもしれませんね。つまり、複数の引数はコンパイラによって分解され、実行時に1つの引数しか取らない関数に変換されて実行されるんだ。次の例は、sum()を1つの引数だけで実装する方法です。実際には、2つの数値を足し算します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/fbbca261-a89f-3ba7-35d4-e1c2a794a93d.png)


一見魔法のように見えますが、unary function は再帰的に複数の数値を足すことができます。このテクニックは、引数の数を最大でも1つに制限するcurryingと呼ばれるものです。簡単に言うと、curryingは引数の関数化である。Curryingの反意語はUncurryingで、defunctionalization　の形を意味する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/eff9abc5-1eb4-8914-34a3-1ec4517f6557.png)



curryingの利点は、コード中の部分的な適用を可能にすることである。遅延評価は引数の使い方によって実行タイミングを遅らせますが、引数が関数の場合のみ実現可能です（質問1の最初の例参照）。つまり、遅延評価を実現するためには、curryingによる関数化が不可欠なのです。

# 部分的な適用 - Partial application



一般的な命令型言語において、ある関数にすべての引数を渡す必要がある。

```js
a = getA() // 1
b = getB() // 2
sum(a, b) // 3
```

しかし、この方法では、すべての引数が揃うまでsum()を呼び出すことができないので不便である。一方、Haskellでは、引数を一つずつ渡していき、それが必要になるまで実行を遅らせることができる。

```js
a = getA() // 1
// bは時間がかかるので、aを先に置く
tmpFuncion = sum(a) // 部分的に実行する。戻り値は関数である。

... // 長い処理
b = getB() // 最後にb = 2を得る
tmpFunction(b) // 残りの処理が実行される
```

# まとめ

関数型言語には、命令型言語とは異なる性能を向上させる戦略がある。currying、高階関数、遅延評価の考え方は、Javascriptのコールバック関数など、他の言語でも活用されている。Haskellでは反復処理(loop)が使えないなどの制限があるが、そのようなハンディキャップはなく、むしろ可読性や性能を向上させる可能性がある。



# Reference

Tutorialspoint. (n.d.). Functional Programming - Lazy Evaluation. Retrieved on May 23, 2022, from https://www.tutorialspoint.com/functional_programming/functional_programming_lazy_evaluation.htm

Tsoding. (2020). YouTube. Retrieved on May 23, 2022, from https://www.youtube.com/watch?v=E5yAoMaVCp0&feature=emb_title


Elliott, E. (2017). https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99. Retrieved on May 23, 2022, from https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99