Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ken-okabe/ea64d8b7014ee934d45c to your computer and use it in GitHub Desktop.
Save ken-okabe/ea64d8b7014ee934d45c to your computer and use it in GitHub Desktop.
「純粋関数型」 &「副作用」というトリッキーワードについて想うこと『裸の王様』
[関数型言語のウソとホント](http://qiita.com/hiruberuto/items/810ecdff0c1674d1a74e)
に引き続き、
[純粋関数型JavaScriptのつくりかた](http://qiita.com/hiruberuto/items/810ecdff0c1674d1a74e)
という、とても有意義な記事を @hiruberuto 氏が連発しておられます。
この一連の記事を起点にして思うことをいくつか共有します。
-----
肝心の「副作用」の説明もしますが、
まずは、いつものように随想みたいになります。
技術分野の発展でもっとも根幹となる、
「視点」や「思想」あるいは「他人が考えていること」に全く興味がない人、
頭を使うのが嫌で、「結果」や「事実」にしか興味がなく、
ネガティブニュンスにおいて
「ポエム」だの繰り返す人は、いつもの如く不快でしょうから、
このまま記事をシェアして不快を拡散することもないし、
そもそも読む義務も義理もないのだしページを静かに閉じれば良い、
ということになります。念の為。
------
##誰にむけての何のための説明か?裸の王様 
昨今の関数型パラダイムにともなって、
「参照透過」や「副作用」の概念の重要性が認識され、
こういう用語の露出が高まってきました。
「副作用」っていう言葉で、一般人がまず思い浮かべるのは、
医薬品の[副作用](http://ja.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8)のことだと思います。
>副作用 (ふくさよう、Side Effect) とは、医薬品の使用に伴って生じた治療目的に沿わない作用全般[1]を指す。狭義には、医薬品の使用に伴って発現した好ましくないできごとのうち当該医薬品との因果関係が否定できないものを指す。この好ましくない作用を厳密に指す場合には、薬物有害反応(Adverse Drug Reaction:ADR)の用語が用いられる。一般に副作用といった場合には、両者が混合して用いられている。
プログラミングの世界の「副作用」はもちろん違う意味で、
[副作用 (プログラム)](http://ja.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8_%28%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%29)
>プログラミングにおける副作用(ふくさよう)とは、ある機能がコンピュータの(論理的な)状態を変化させ、それ以降で得られる結果に影響を与えることをいう。
とか、書いているわけですが、よく意味がわかりません。
よくわからないのは、閲覧者の知性が劣るからではなくて、
ほとんどの場合(そしてこのWikipedia記事でも)、
説明するほうの知性に問題があります。
これ読んで意味がわかる人は、おそらく世界中探してもひとりも存在しません。
なぜならこの定義はデタラメだからです。
さらに、このWikipedia記事では、
引き続き「参照透過性」が出てきて、
この「参照透過性」という概念とあわせて
「副作用」という概念を説明しようと試みています。
こういう説明の仕方は最悪です。
新しい概念を説明するときに、
別の新しい概念をもって説明することをやってはいけません。
基礎も土台もなく、空中で家を組み立てるに等しい愚行です。
このWikipedia記事だけがそうであるならば、
じゃあ文句たれるオマエがさっさと修正編集しろよ、
ってことになるわけですがそういうことではありません。
本当の問題は、世の中にあふれるプログラミングの説明は、
同じようにどれもこれもわけがわからないものばかりで、
このWikipedia記事と同じだ、ってことです。
「副作用」の説明に限らず、プログラミング全般の説明をみていて、
「ねえ君、これ一体誰にむけて何のために説明してんの??」
って思うことがよくあります。
説明する人間っていうのは、そもそものスタート地点では、
**「わかりやすいように説明したい」**
みたいな意欲が必ずあるわけです。
でも説明しているうちに必ず「不安」になってきます。
どういう不安か?
**「馬鹿に思われたくない」**
っていう不安です。
自分は説明するほうで、相手は説明される側なので、
自分のほうが立場が上のはずだし、
相手にちょっとでも「馬鹿だ」と思われたら自分の言葉に重みがなくなる、
そうなれば相手は自分の説明なんてまともに聞かなくなるだろう、
だからメンツを何がなんでも維持しなきゃいけない。
こういう「馬鹿に思われたくない」という「不安」は
多かれ少なかれどの説明者、教師も持っています。
もちろん筆者も例外ではありません。
で、多くの説明者、教師がこの
「馬鹿に思われたくない」っていう「不安」
を解消するためにどうするか?っていうと、
**なんと「武装」しちゃうんですね。**
誰にたいして武装するかっていうと、
もちろん説明している生徒に対してです。
あと「ギャラリー」にたいしても武装します。
こいつは偉そうに説明しているが、実は何にもわかっちゃいないよ(笑)
みたいな批判をたいへん恐れるんですね。
だからたいていガチガチに武装しはじめます。
具体的にどういう武装をするか?っていうと
とりあえずは難しい言葉を使ったりします。
**とりあえず難しい言葉なら相手は理解しにくい、**
**理解しにくいものならば、馬鹿にされるリスクも抑えられるだろう!**
もはや、その「説明」っていう行為が、
**相手に理解させるためにやっているのか?**
**相手に理解させないためにやっているのか?**
いったいこの人は何をやっているのか?わけがわかりません。
ただただ「説明するもの」が「馬鹿」に思われないようにする、
というプライドが優先されるんですね。
こういうのを**「本末転倒」**と言います。
もはや**当初の「説明」なんていう目的は二の次**になってしまう。
**説明<<自分のプライド**
みたいなことになっていく。
こんな体たらくで、
「わかりやすいように説明したい」
という大目的が達成されるわけもありません。
あと「武装」の方法としては、
さっきのWikipedia記事みたいに
「参照透過」という**同カテゴリに属する別の新規概念を持ち出して、**
**説明を補完**しようとします。
これも「わかりにくい言葉」という武装になるし、
とりあえず、別用語、別概念に、
**元来この説明者がしないといけない説明を外部に丸投げ**してしまいます。
**この説明でわからないのは自分の責任ではない**、と。
「参照透過」の意味がほんとうにわかっている人のほとんどは、
「副作用」の説明なんて必要ないわけです。
じゃあ、わかった。
「副作用」を知るにはとりあえず
「参照透過」は知っておかなければいけない言葉なのだろう。
そう思って「参照透過」を調べると、
なんとそこでは「副作用」という言葉をもって説明されている!
茶番です。
「参照透過」も「副作用」も両方の意味がわからない人が、
「副作用」の意味を知りたいって思っているんですね。
そういう相手にむかって「参照透過」で「副作用」を説明する。
「副作用」で「参照透過」を説明する。
あなたはいったい誰に向かって何の目的でそういうことを書いているんですか?ってことになる。
プログラムの世界で延々と繰り返される説明の大方はこういう説明です。
わざわざわかりにくいように説明されている。
だからわかりにくい。
それはおそらく説明している人自体が実際わかっていないから、
自分がうまく説明なんてできるはずもないって心底気づいているから、
わかってないことがバレないように
馬鹿に思われないように
批判されないように武装する必要が出てくる。
**一番の問題は、閲覧者がそういうのをありがたがること**なんですね。
わかりにくい言葉で、わかりにくいような説明を、
**わかったように振る舞うことが自分の知性の証明だみたいに振る舞っている。**
こういうの聞いたことありますよね?有名な逸話
**『裸の王様』**です。
##副作用とは?
[副作用 (プログラム)](http://ja.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8_%28%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%29)
は、医薬品の[副作用](http://ja.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8)
と言葉が同じであるとおり、根本は同じ概念です。
名は体をあらわす。
この「作用」っていうのは、
プログラミング、数学の「関数」のことです。
[関数型プログラミングに目覚めた!IQ145で美少女JKの先輩から受けた特訓 5日間](http://qiita.com/kenokabe/items/618692858044a89adbc0)
でも説明しているので、ピンときにくい人はあわせて読むといいでしょうが、
 
**数学・プログラミング用語として、英語ではfunction、それを和訳したものが関数**で、
そもそもの**functionとは、機能、動作、操作、作用、という意味**です。
![関数イメージ画像](http://upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Function_machine2.svg/485px-Function_machine2.svg.png)
この図では、
x
↓f
f(x)
という構図になっています。
関数fは、何でもよくて、
たとえば炊飯器であるならば、
米と水
↓炊飯器
炊きたてのご飯 = 炊飯器(米と水)
時間変化に着目して、BeforeAfterで言い換えるならば、
Beforeが、様々な問題を抱えた家 
で「匠」という関数が作用する
After は、なんということでしょう!ってなるリフォームされた家
様々な問題を抱えた家 
↓匠
なんということでしょう!! = 匠(様々な問題を抱えた家)
医薬品が関数ならば、
病気の身体要素
↓医薬品
治癒された身体要素
です。
医薬品の副作用っていうのは、
病気の身体要素
↓医薬品
治癒された身体要素  + 作用対象外なのに作用変化した身体要素
ってことです。
炊飯器の副作用であるならば、
米と水
↓炊飯器
炊きたてのご飯   + 勝手に隣の鍋のお湯が沸騰する
劇的BeforeAfterの副作用ならば、
様々な問題を抱えた家 
↓匠
なんということでしょう!!  + 依頼されていない隣の家がリフォーム
ってことです。
「関数」に
「入力」**されていない要素**が、
「出力」とも関係ない場所で変化する、っていうのが
「副作用」です。
##プログラミング世界の「副作用」の説明について
さて、プログラミング世界の「副作用」の説明についてです。
しょっちゅう見るのが
**IO(入出力)は副作用だ**、っていう説明。
上でやった副作用の説明とつきあわせて、
この意味がわかる人がいるでしょうか?
少なくとも筆者は、何のことかさっぱりわかりませんでした。
だって、本来の副作用の定義からはそんな帰結に結びつくわけがないから。
あと、**プログラミングは副作用がないと何もできない**、
っていう言い回し。
これも結構わけわからないですね。
だって副作用がなくたって作用しているのならば、
ちゃんと何かしているわけじゃないですか?
**念のためですが、以上は広く観察される一般論です。**
しかし、一部
[純粋関数型JavaScriptのつくりかた](http://qiita.com/hiruberuto/items/810ecdff0c1674d1a74e)
にもあてはまります。
>言語全体が参照透明な(簡単にいえば副作用のない)式で構成される言語を純粋関数型プログラミング言語と言います
と最初に書かれています。
「言語全体が参照透明」
なるほど値が変化しない論理の整合性の話か!
と想う。ここまでは良い。
「簡単にいえば副作用のない」
??となる。
「参照透明」っていうのは、
それ自体でかなり簡潔に説明できている概念です。
そしてこれと
「副作用」っていうのは、かなり別のはなしです。
上で説明したとおりで、
上で説明した「副作用」のことから自然と
「参照透明」のことに考えが及ぶ人なんてまずいないでしょう。
**「参照透明」**なんていう簡潔な概念が、
上で長々と説明したような
**「副作用」があるのか?ないのか?**
という事情をもって説明されるという
妙ちきりんな説明を好む人が山ほど存在するという事実、
は筆者ももちろんよく知っています。
しかし、妙ちきりんな説明をする人が山ほど存在するという事実は、
それが「簡単な説明」になるという事実にはなりません。
すくなくとも私はそれが「簡単な説明」だと思ったことは
これまでに一度たりともないし、今でもわけがわからない、
って思っています。
で、 
[純粋関数型JavaScriptのつくりかた](http://qiita.com/hiruberuto/items/810ecdff0c1674d1a74e)
をずーっと読んでいくうちに、猛烈な違和感が生じてきます。
どうもここで言われている「副作用」とは、
上で説明したような、本来の論理世界の関数操作による「副作用」
のことではなくて、時間変化に伴う、
完結した論理世界外部とのやりとりのことだということがわかってくる。
具体的にはIO(入出力)のことを「副作用」と呼称している。
>本当に何もかも副作用がなかったら、プログラムは何もすることができません。清浄なる世界を取り戻すためには、誰かが汚れ仕事を引き受けなければなりません。
IO(入出力)というのは、本来、論理世界の外にある事象なので、
原理的に「副作用」になりますが、
「副作用」は必ずしもIO(入出力)ではないのは上の説明でもわかることです。
「副作用」=IO(入出力)ではありません。
```
var a = true;
var f = function(x)
{
a = false;
return x;
};
```
x
↓f
x
という作用に伴って、
`a`が`true`から`false`に変化する**副作用がある**コードですが、
IOとは何の関係もありません。
後これに関連して、もっとひどいものもあって、
「副作用」が値を変化させること、
「副作用=ミュータブル」
みたいに言う人がゴロゴロと存在します。
あきらかに間違いです。
>言語全体が参照透明な(簡単にいえば副作用のない)式
っていうのは、 
値の変化がない(イミュータブル)参照透明な世界は、
「副作用=ミュータブル」がない←間違い
って説明されていると捉えられます。
いったいいつから「副作用」っていう概念、用語が
「バズワード」になったのでしょうか?
IO(入出力)はIO(入出力)と書くべきであり、
IO(入出力)をプログラミングで扱うときに
安易に「副作用」と代名詞のように使うのはやめたほうが良いと思います。
根本的な問題は、
ほとんどのプログラマが
「論理」ソフトウェア
「物質」ハードウェア
の話をプログラミングの世界でしたがらないことです。
なんでかはよくわかりません。
おそらく彼らがこれまで読んだ本にこういうことが書かれていないからなんじゃないですかね?
UNIXの時代には、もっとみんなちゃんと語っていました。
UNIXの標準入出力装置
標準入力 キーボード
標準出力 ディスプレイ
>本当に何もかも副作用がなかったら、プログラムは何もすることができません。清浄なる世界を取り戻すためには、誰かが汚れ仕事を引き受けなければなりません。
っていう意味は、要するに、
CONSOLE ディスプレイなどのハードウェアの文脈で
「副作用がなかったらプログラムは何もすることができない」
と書かれているわけです。
しかし、「副作用」とか「参照透過性」というのは、
あくまでソフトウェア=コードの論理的整合性の話であって、
ハードウェアの話では**ありません**。
「参照透過性」という論理的整合性を
ハードウェアのIO(入出力)≠副作用として語るのはおかしいわけです。
「関数」に
「入力」**されていない要素**が、
「出力」とも関係ない場所で変化する、っていうのが
「副作用」です。
ブラウザのDeveloperツールなどでやると、こうなります。
>console.log('hello');
undefined
"hello"
`console.log('hello');`
で入力された`hello`は論理世界では消滅し、
`console.log`の返り値は`undefined`です。
標準出力 ディスプレイ というハードウェアで
`hello`が出力されました。
ハードウェアっていう物質世界は論理世界の管轄外なので、
副作用だってことです。
##純粋関数型言語の定義
こういうIOに伴う副作用をIOモナドをもって
論理世界と隔離するのを純粋関数型、と定義するのには、かなり異論があって、以下、コメント欄に書いたことも再掲すると、
日本語のWikipediaでも、HaskellWikiにしてもそういうことが書かれているわけですが、特に、日本語版Wikipediaの純粋関数型の定義については、
筆者が見渡す限り出典が存在しませんでした。
いったい誰がどういう理路をもってそういうことを書いたのか?全く不明であるということです。
>別の定義では、純粋関数型言語/論理プログラミング言語/制約プログラミング言語で書かれたプログラムを「宣言型」と称する。この立場では、「宣言型プログラミング」とはそのようなプログラミング言語のグループ全体の総称であり、命令型言語と対立する概念である。
この文章の解釈ですが、
- 個個の具体的な言語実装から「宣言型」という概念が醸成されていく、
というのは、これも結構意味がわからない考え方であって、
- 「宣言型」という概念を具現化しているのが、個個の具体的な言語実装だ、
と読み取るほうが合理的なので、誤解を招く文章に問題があります。
一方で、英語版Wikipediaの記事ですが、
http://en.wikipedia.org/wiki/Purely_functional
>In computing, algorithms, data structures, or programming languages are called purely functional if they guarantee the (weak) equivalence of call-by-name, call-by-value and call-by-need evaluation strategies, often by excluding destructive modifications (updates) of entities in the program's running environment.[1] According to this restriction, variables are used in a mathematical sense, with identifiers referring to immutable, persistent values.
Purely Functionalであることの定義として冒頭部分で、
>programming languages are called purely functional if they guarantee the (weak) equivalence of call-by-name, call-by-value and call-by-need evaluation strategies
プログラミング言語は`equivalence of call-by-name, call-by-value and call-by-need evaluation strategies` つまり非正格の評価戦略が保証されているとき純粋関数型である、と定義されています。
これには出典が存在し、
http://en.wikipedia.org/wiki/Purely_functional#cite_note-1
[What is a purely functional language?](http://journals.cambridge.org/action/displayAbstract?fromPage=online&aid=44143)というケンブリッジ・ジャーナルの論文です。
>Abstract
Functional programming languages are informally classified into pure and impure languages. The precise meaning of this distinction has been a matter of controversy. We therefore investigate a formal definition of purity. We begin by showing that some proposed definitions which rely on confluence, soundness of the beta axiom, preservation of pure observational equivalences and independence of the order of evaluation, do not withstand close scrutiny. We propose instead a definition based on parameter-passing independence. Intuitively, the definition implies that functions are pure mappings from arguments to results; the operational decision of how to pass the arguments is irrelevant. In the context of Haskell, our definition is consistent with the fact that the traditional call-by-name denotational semantics coincides with the traditional call-by-need implementation. Furthermore, our definition is compatible with the stream-based, continuation-based and monad-based integration of computational effects in Haskell. Finally, we observe that call-by-name reasoning principles are unsound in compilers for monadic Haskell.
//
>Functional programming languages are informally classified into pure and impure languages.The precise meaning of this distinction has been a matter of controversy.
関数型言語は`informally`に`pure`か`impure`じゃないか分類されている。正確な差異の意味については議論の対象となってきた。
つまりこの論文が論じる時点で、明確なコンセンサスが存在しないということですね。
>We propose instead a definition based on parameter-passing independence. Intuitively, the definition implies that functions are pure mappings from arguments to results; the operational decision of how to pass the arguments is irrelevant.
要するに評価戦略によって純粋関数型を定義する、という論文です。
> In the context of Haskell, our definition is consistent with the fact that the traditional call-by-name denotational semantics coincides with the traditional call-by-need implementation.
Haskellのコンテクストで言えばcall-by-name,call-by-need(遅延評価)による定義で整合性が取れる。
> Furthermore, our definition is compatible with the stream-based, continuation-based and monad-based integration of computational effects in Haskell. Finally, we observe that call-by-name reasoning principles are unsound in compilers for monadic Haskell.
それに伴い、この定義は、Haskellのモナド統合にも援用できる・・・
ということです。
これは全体的に上で私が論じていることと全く一緒のことで、
理論的にはまず、遅延評価による宣言型であるというのが先立ち、
遅延評価ならば、理路によって宣言型となるだろうし、
宣言型の関数型ならば、純粋関数型、
遅延評価⇒純粋関数型であるということです。
>「宣言型」というのは、「処理方法ではなく対象の性質などを宣言することでプログラミングするパラダイム」もしくは単に純粋関数型であるというのと2種類の意味で使われるようです。
たしかにWikipediaにそう書かれていますが、これもやはり誰がどういう理路でそういうことを言ったのか不明です。
普通に考えても、
「宣言型」というのが、ろくにコンセンサスが存在しない「純粋関数型」によって定義される、
あるいは、
>私が使っている方の定義では、参照透過性が維持されればそれは「純粋」で「宣言型」なのです。
というのは、なかなか意味がわからない話であって
(少なくとも一体全体どういう理路でそんなことになるのか納得もできず、まったく説明もなされず、理解もできない)、「宣言型」のほうがかなり明確に評価戦略と表裏一体になった道理で様態がわかるわけです。
少なくとも、先行評価で宣言型になる、とかまずありえないです。
ここは私も文章を割いて明確に説明しているとおりで、理路が不明な誰かの定義で上書きされてしまう、というのはありえません。
ですから、宣言型の関数型ならば、純粋関数型、という定義のほうが理論的にも整合性が取れます。
##HaskellのIOモナドは、遅延評価でFRP
それを踏まえ、この記事はかなり興味深かったです。
私は前々から、HaskellのIOモナドっていうのは、
単に遅延評価でFRPのデータバインディングの限定版のことだろう?
って漠然と思っていて、
それ以上は興味もなく深く掘り下げることはなかったのですが、
今回実際に、非常に簡潔明快にJavaScriptに実装していただいたので、
おかげさまでそれが追認できました。
>## なにが起きてるの?
>1. `get("http://fiddle.jshell.net/")` を呼び出すと `function(){ var xhr = new XMLHttpRequest(); ... }` みたいな関数が返ってきます。この関数は引数がない関数で、呼び出されるとXHRを発行してデータを取ってきます。が、まだその時ではありません。じっと時を待ちます。この実装では、アクションとはこういう引数がなくて実行の時を今か今かと待ち構えている関数のことです。
2. `x=>put(x.toUpperCase())` は文字列 `x` を渡されると `toUpperCase` で大文字にして `put` に渡す関数です。でもまだその時ではありません。`put` に渡す文字列が来るまでじっと待ちます。雌伏のときです。
3. `bind(...)(...)` で、1と2で作られた関数オブジェクトが`bind`に渡されます。`bind`はこういうアクションと関数を結びつけた、新しいアクションを返します。しかしあくまで結びつけるだけで、何もしません。ひたすら時を待ちます。
4. `main`に3で作られたアクションが代入されます。AurorScriptのコードはここまでです。ここまで何の副作用もありません。これが言語が参照透明である、純粋関数型言語である、ということです。
5. しかし`main`に渡されたアクションはそのままAurorScriptのランタイムに渡され、AurorScriptランタイムは`exec`を使ってこのアクションを起動します。時が来た!結び付けられたアクションが順番に起動し始めます。
5. `bind`内部でまず`get("http://fiddle.jshell.net/")`のアクションが起動されXHRを発行、完了すると`bind`は結果の文字列を`x=>put(x.toUpperCase())`に渡します。
6. `x=>put(x.toUpperCase())` の `x` にさっきの文字列が渡され、`toUpperCase` で大文字になり、`put` に渡されます。
7. この`put("文字列")` はアクションですが、これも`bind`内部で起動されます。`console.log`が呼び出され、文字列が出力されます。
この詳細な解説を拝読すると、
>じっと時を待ちます。この実装では、アクションとはこういう引数がなくて実行の時を今か今かと待ち構えている関数のことです。
>でもまだその時ではありません。`put` に渡す文字列が来るまでじっと待ちます。雌伏のときです。
>しかしあくまで結びつけるだけで、何もしません。ひたすら時を待ちます。
>このアクションを起動します。時が来た!結び付けられたアクションが順番に起動し始めます。
これはどこからどう読んでも、遅延評価のことであり、
イベント駆動のFRPのデータバインディングのことです。
実は以上と同等なものはJavaScript-ES6で標準化される`Promise`で簡単に出来ます。
```js
var def = Promise.defer();
def.promise.then(console.log);
def.resolve(123) // 123
```
他に「Promiseはモナドである」と論証している人もいます。
[モナドについて考えた記録](http://javascripter.hatenablog.com/entry/2013/11/02/%E3%83%A2%E3%83%8A%E3%83%89%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%80%83%E3%81%88%E3%81%9F%E8%A8%98%E9%8C%B2)
>Promiseってなんとなくモナドっぽいなと思って、ジェネレータ使ってHaskellのdoを再現できないかなあというのがはじまり。結論からいうと、できない。
そもそもPromiseがモナドであるかを考える。
JavaScriptのコードで数学的に論証
>よって、Promiseはモナドである。
Promiseっていうのは、future, promise, delayというように、
当然もともと遅延評価の文脈で出てきたもので、
そして同時に、PromiseはFRPです。
正確には、FRPのごく一部の実装。
>ここまで何の副作用もありません。これが言語が参照透明である、純粋関数型言語である、ということです。
ということですが、私はこれまで何度か別のところで書いているのですが、
「副作用」をもって純粋関数型を定義するって、かなり筋が悪い、
というか、論理の理路として正直さっぱり意味がわからないわけで、
遅延評価、FRPならば純粋な宣言型であることと同値で、
それがすなわち純粋関数型である、というのがしっくりきます。
##でも秀逸な記事だと思う
前半書いた用語の問題、説明の仕方の問題、
アロー関数表記で、nodeでも動きづらい、
おそらくFirefoxブラウザでしか動作しない、
動作環境が限定されており、JsFiddleのサンプルも何故か動作しない、
できたら、HelloworldはURLにアクセスするようなものではなくて、
ほんとにHelloworldだけにして欲しい、という要望はいくつかあるのですが、
総じて参照価値の非常に高い秀逸な記事だと思いました。
特にこのコードの証明
http://aurorscript.orionhub.org:8000/aurorscript.js
```js
"use strict";
// -- stdlib --
var pure = a=>_=>a // pure :: a -> IO a
var bind = m=>f=>_=>f(m())() // bind :: IO a -> (a -> IO b) -> IO b
var exec = m=>m() // exec :: IO a -> a
var wrap = f=>a=>_=>f(a) // wrap :: (a -> b) -> (a -> IO b)
var put = wrap(console.log.bind(console)) // put :: a -> IO ()
var get = wrap(url=>{ // get :: string -> IO string
var xhr = new XMLHttpRequest()
xhr.open("get", url, false)
xhr.send()
return xhr.responseText
})
// -- runtime --
Object.defineProperty(window, "main", { set: exec });
/*
A proof that (pure,bind) is a Monad:
law1: (return x) >>= f == f x
bind(pure(x))(f)
= (m=>f=>_=>f(m())())(pure(x))(f)
= (f=>_=>f(pure(x)())())(f)
= _=>f(pure(x)())()
= f((pure)(x)())
= f((a=>_=>a)(x)())
= f((_=>x)())
= f(x)
bind(pure(x))(f) = f(x)
law2: m >>= return == m
bind(m)(pure)
= (m=>f=>_=>f(m())())(m)(pure)
= ( f=>_=>f (m())()) (pure)
= ( _=>pure(m())())
= ( _=>(a=>_=>a)(m())())
= ( _=>(_=>(m())) ())
= ( _=>((m())) )
= _=>(m())
bind(m)(pure)() = m()
bind(m)(pure) = m
law3: (m >>= f) >>= g == m >>= (\x -> f x >>= g)
bind(bind(m)(f))(g)
= bind( bind (m) (f) )(g)
= bind( (n=>h=>_=>h(n())()) (m) (f) )(g)
= bind( ( h=>_=>h(m())()) (f) )(g)
= bind ( _=>f(m())()) (g)
= bind (_=>f(m())()) (g)
= (n=>h=>_=>h (n())()) (_=>f(m())()) (g)
= (n=>h=>_=>h( n ())()) (_=>f(m())()) (g)
= ( h=>_=>h( (_=>f(m())()) ())()) (g)
= ( _=>g( (_=>f(m())()) ())())
= ( _=>g( f(m())() )())
= (_=>g(f(m())())())
bind(m)(x => bind(f(x))(g))
= bind(m)(x => (n=>h=>_=>h(n())()) (f(x)) (g))
= bind(m)(x => (n=>h=>_=>h(n ())()) (f(x)) (g))
= bind(m)(x => ( h=>_=>h((f(x)) ())()) (g))
= bind(m)(x => ( h=>_=>h((f(x)) ())()) (g))
= bind(m)(x => ( _=>g((f(x)) ())()) )
= bind(m)(x => (_=>g((f(x))())()))
= (n=>h=>_=>h(n())()) (m) (x => (_=>g((f(x))())()))
= ( h=>_=>h(m())()) (x => (_=>g((f(x))())()))
= ( h=>_=>h (m())()) (x => (_=>g((f(x))())()))
= ( _=>(x => (_=>g((f(x))())())) (m())())
= ( _=>(x => (_=>g((f(x ))())())) (m())())
= ( _=>( (_=>g((f((m()) ))())())) ())
= (_=>((_=>g((f((m())))())()))())
= (_=>(_=>g(f(m())())())())
= (_=>g(f(m())())())
bind(bind(m)(f))(g) = bind(m)(x => bind(f(x))(g))
*/
```
は、素晴らしいし、FRPの実装を単純に、
いわゆるObserver実装でしか考えていなかった自分にとっては、
とてもありがたかったです。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment