「参照透過性」という用語は裸の王様と言えない愚者のための概念

関数型プログラミングの本を書いた。

2021年末に基本フリーで誰でも読める形で関数型プログラミングの入門書をUPした。構想だけをしていたり、トライアンドエラーでゼロから書き直したりする時間全部ひっくるめると2年ほどかかった。かなりの長編ではある。

関数型プログラミングが『銀の弾丸』であるという非常識な常識2022

ここで、少なくともこのフェイズで2年間ほど、どうやって初学者に関数型プログラミングの概念を必要十分に伝達できるだろうか?ということを結構僕なりに真剣に考えてきたと思う。

そして、この2年より前から2015年頃から、まあだいたい7年ほどかけて痛感しているのは、「参照透過性」という用語は裸の王様と言えない愚か者のための概念である、ということだ。

ここで説明しよう。というか2022年になる前に説明しておきたい。

なぜならば、2021年末この著作をUPしてから、馬鹿がTwitterネガキャンはじめて、2015年のQiitaの馬鹿げたネガキャン騒ぎについて再言及してきたからだ。

やっぱ放置しておくのはよくないよね。

 

すでに本ブログで書いた、これ、である。

 

ken-okabe.hatenablog.com

 

 

 

ここで、この馬鹿がいう「みんな」とは、同列の馬鹿のことであり、「参照透過性の理解」というのは、この馬鹿どもが参照透過性なんて言葉を理解なんてしていない、あるいは理解したつもりになっているが、なんのことか本当にわかってるわけもない、あるいは「王様は裸だ」と言えない愚か者である、ということを意味する。

🤣🤣🤣🤣🤣🤣🤣

と逐一追加しておかないと、ツイート一つするにも精神が安定しないのか、非常にアレ界隈のアレであることは見て取れる。

だいたい参照透過性界隈のおバカさんはこういう感じなのだろう。

 

まず、前回も論じたが、

 

ken-okabe.hatenablog.com

 

これが納得いった説明だとかよく言われたりする。

web.sfc.keio.ac.jp

 

f:id:stq2050:20211229174449p:plain

 

以上の説明で、最初の3つ

  • プログラムは関数定義の集合であり、関数呼び出しによってそれらを組み合わせる。

  • 関数は first class object である。

  • 文という単位は無く、プログラムの実行とは式を評価することである。

はまあまあ良い。

特に、3つ目の、

  • 文という単位は無く、プログラムの実行とは式を評価することである。

というのは、著作でも最初から最後まで一貫して強調していることだ。

上の説明が「駄目」なのは、

  • プログラムは関数定義の集合であり、関数呼び出しによってそれらを組み合わせる。

  • 関数は first class object である。

という最初2つが、その3つ目に統合されるので、冗長な説明である、ということだ。

はっきとした定義があろうがなかろうが、論理的に冗長であって統合できるのであれば、それは統合して構造化して初学者に提示すべきなのだが、「はっきりとした定義がない」という権威(オーソライズ)されていないと見るや否や思考停止というか、巷で言われていることを適当に列挙してそれで誤魔化してしまう

書くなら以下のように構造化して提示すべきだろう。

1.関数型コードとは式だ。←これがメインテーゼ。

2.そして二項演算、つまり二つの式をくっつけてつなげる、という「式」がほとんどすべてで、その二項演算とは実は二項の関数と同じものだ。

3.そしてその式=値には関数も含まれる、従って、関数を値として取り回せること=FirstClassOjectである必要がある。

こう書くべきだろう。

  • プログラムは関数定義の集合であり、関数呼び出しによってそれらを組み合わせる。

とかいうのは、「関数型プログラミング」という言葉なので、とりあえず「関数」については言及しておかなければいけないだろう、という書き手の安易な発想であり、

正確には、

  • プログラムは式の集合である。

からはじめるほうが正確だし、

「関数呼び出しによってそれらを組み合わせる。」

というのは、関数合成、高階関数のことをざっくり言ってるつもりなのかはしらないが、「関数呼び出し」によって、それらの組み合わせが生じるというのは概念的にはデタラメで穏当にいっても冒頭の定義としては適当すぎる。

 

そして問題の箇所にさしかかる。

 

f:id:stq2050:20211229180102p:plain

まず、何が致命的に駄目なのか?

というと、基本的にこの書き手、解説者は、初見者にむけて概念を教えるつもりなんてない、ってことだ。

 関数型言語とは

 

とかいう、初見であると想定される未知の概念説明のときに、別の未知の概念を利用してはいけない。

理解とはすでに理解している概念の地道な拡張であり、無理解の上に無理解の概念を重ねて理解が生まれるはずない。原理的に。

そして何より、関数型プログラミングの理解のために、本当にこの「参照透過性」なることば、概念が必要なのか?

最も重要なのは参照透過性というのは本当なのか?

という疑念がある。もしこれが嘘であるならば、まったく本質でない概念に依存する形で、未知の概念を構築しようとしている。土台がない、ってことだ。

結論をすでに書いたので、「参照透過性」なんてものは関数型に最も重要な概念でもなんでもないので、嘘の説明の仕方をしている。

 

f:id:stq2050:20211229180857p:plain

なるほど。前回のエントリで僕はこう書いた。

1+2=3

とかいう小学校の頃から慣れ親しんでいる二項演算は、二項関数と等価であり、この式こそが関数型コードだ、みたいな説明こそが大事だからです。

自分が読んだ本、あるいはこういう持ち上げられている解説ページに、小学校の頃からやってる二項演算が関数型コードの式である、という単純明快な解説がなされているのをこれまで見たことがありません。

そして二項演算ってのは、数学の定義上、ここで言われる参照透過性があるわけですが、誰が、これまで二項演算やるのに、参照透過性が重要だ、みたいな教育を受けたことがあるのでしょうか?

1+2=3

という二項演算の式=関数型コードのあり方を理解するためには

「参照透過性」とは完全に不要な用語です。

 

こういう説明を読んでいる閲覧者は例外なく自身の体験として理解していることだろうけども、これまで彼ら、我々が義務教育からはじまっておそらく大学教育を終えるまでに、あらゆる分野の数学を学習するときに「参照透過性」なんて言葉は学校で教えられたことなど一度もなかったはずだ。

なんか、数学と独立した範囲内で、なぜかプログラミングの分野だけに、特に関数型プログラミングの初紹介を受ける際に限って、この珍妙な「参照透過性」がもっとも重要だ、みたいに言われる。

だってね、

式の構成要素がすべて同じなら、式の値は常に同じになるということ。

なんてことは、数学ではアタリマエのことじゃない(笑

そして、こんな義務教育から一貫してアタリマエの作法として受け入れられてきたことに、これまで教師は一切「特別な名称」を与えることなどしなかったし、我々生徒は、それが何かのもっとも重要な概念だ、みたいに特別扱いされたステージで概念認識することもなかったはずだ。

  • これの意味するところは、式の値を計算するのに、その時点での状態というものを考慮する必要がないということである。例えば、もし f(x) という式の評価結果が2になったとしたら、 f(x) を何回呼び出しても常に2を返す。

これも結局同じことを書いてるよね。

  • 手続き型言語に参照透過性がない原因は、変数の値が途中で変わることと、式の構成要素以外のもの(大域変数など)に依存した計算ができることである。

そして、こういう説明が悪い。

結局の所、著作でも書いたとおり、手続き(命令型)プログラミングでは、数学ではアタリマエであり続けた作法を捻じ曲げているわけだが、その事を

手続き型言語に参照透過性がない原因」

つまり、アタリマエではなくしている原因、という説明に、「参照透過性」という無用に新しく登場した用語をもって、原因がどうのと説明している。

わかりますか?

XXには以下のような特性があります。

まずここに、ホニャララという概念があり、それがもっとも重要なのです!

ホニャララはこれまで我々がアタリマエのように理解して実践してきたことです。

XXでないものが、ホニャララでない原因は、アタリマエじゃなくなっているからです。

 

だいたいこういう論理構造の説明の仕方で、このホニャララとかいう言葉っている?

XXとその他の「比較」アタリマエに知っていることの「差異」として紹介されるのならともかく、わざわざ耳慣れない命名された特別な概念として、もっとも重要だ、みたいにしなければいけない?結構狂った世界ではある。

 

大枠では

1.義務教育以来、理解して実践してきた、「式の構成要素がすべて同じなら、式の値は常に同じになる」というアタリマエの数学の知識、作法

2.命令型というそれがアタリマエでなくなった作法

3.関数型というアタリマエに戻す作法

という流れがあるわけだが、この至極単純な事実を説明するために

「参照透過性」ということばをデッチ上げて、学習者惑わせているわけである。

そして、多くのプログラマはこの「参照透過性」という混乱を招いているだけの無駄で無意味なことばを「最重要」と信じてやまない。

なんでか?こういう説明でもそう書かれているし、

「みんながそういってるから」だよね。つまり

裸の王様と言えない愚者のための概念だったこと。

こういうやつのこと。

 

 

 

ここで、この馬鹿がいう「みんな」とは、同列の馬鹿のことであり、「参照透過性の理解」というのは、この馬鹿どもが参照透過性なんて言葉を理解なんてしていない、あるいは理解したつもりになっているが、なんのことか本当にわかってるわけもない、あるいは「王様は裸だ」と言えない愚か者である、ということを意味する。

 

あと、

ken-okabe.hatenablog.com

の元記事である、

qiita.com

 

では、同列の愚者がコメント欄に湧いていて、

 

 

Wikipedia の「参照透過性」の項目には

参照透過性(さんしょうとうかせい、英: Referential transparency)は、計算機言語の概念の一種で、文脈によらず式の値はその構成要素(例えば変数や関数)によってのみ定まるということを言う。具体的には変数の値は最初に定義した値と常に同じであり、関数は同じ変数を引数として与えられれば同じ値を返すということになる。

と書いてあります。

またオンライン版の『Learn You a Haskell for Great Good!』にも

if a function is called twice with the same parameters, it's guaranteed to return the same result. That's called referential transparency ...

と書いてあります。

これらの文章を読めば UCLA を卒業した岡部氏でなくても分かると思いますが、関数に関して言えば、「参照透過性(参照透明性)」とは「ある関数に同じ引数を渡せば、必ず同じ値が返ってくること」です。

ところが Data.new() は引数としては何も渡していないのに、毎回異なった値が返ってくるんですよね。これのどこが参照透明なんですか?

それと、どちらかの方(コメントの芸風があまりにも似過ぎていて私には区別がつきかねます)のコメントに

Date.now()の場合、具体的には、暗黙の了解としてユーザの現在時刻Tがnow()の引数になっています。

と書いていますが、そもそも引数とは明示的に関数に渡すものではないですか?

Date.now() がユーザーの現在時刻を元に値を返すなら、それは Date.now() がユーザーの現在時刻を「参照」しているだけではないのですか?

もしかしたら、岡部氏や qiitapost さん、chimetorch さんは私たちとは違う次元に住んでいて同じ言葉でも違う意味になるのかもしれませんね。

異次元に住んでいる人たちと交流できるなんて、Qiita ってすごいですね。

 

あるいは、

 

(編集済み)
 
  • ブログ上でOCamlGUIアプリを作成せよと述べたにもかかわらず、岡部氏自身はFRPを用いた実用的なGUIアプリを提示していない。

  • JavaScriptのDateは関数型プログラミングのストリームであり、.now()関数の返り値も参照透明と主張。

 

あるいは、

 

(編集済み)
 

すみませんが、これまでに何度も具体的に誤りを指摘されてきた岡部氏と全く同じご主張のようですので、これまでの議論をお読みください。特に、参照透明の定義は、岡部氏と全く同じ誤解をされているようですので、英語版Wikipedia
https://en.wikipedia.org/wiki/Referential_transparency_%28computer_science%29
や、英語版Wikipediaから参照されGoogle Scholar
https://scholar.google.com/scholar?q=referential+transparency
でもトップに来る標準的な学術論文
http://www.itu.dk/people/sestoft/papers/SondergaardSestoft1990.pdf
をご覧ください。一つ目の英語版Wikipediaの現時点での記述は(ここでの議論に関連する範囲では)二つ目や三つ目のような数多くの(というか確認できる限り全ての)査読付学術論文と整合的で信頼できます。ご参考までに、例えば以下の記述があります。

today() is not transparent, as if you evaluate it and replace it by its value (say, "Jan 1, 2001"), you don't get the same result as you will if you run it tomorrow. This is because it depends on a state (the time).

(私訳:「today()は参照透明ではありません。評価して値(例えば"Jan 1, 2001")で置き換えると、明日に実行するのと同じ結果が得られないからです。これはtoday()が状態(時間)に依存しているためです。」)

逆に(時間にせよ何にせよ)プログラム上に存在しない暗黙の引数を考えれば参照透明などと言い出したら、状態に依存したどのような関数も参照透明になってしまい「参照透明」という概念の存在意義がなくなってしまいますので、そのような定義があり得るとは思いません。

追記:「ご回答よろしくお願いします」とのことですが、これまでの議論ですでに明白な、岡部氏と全く同じ誤りを繰り返すばかりで、誠に失礼ながらいわゆる「荒らし」が目的としか思えませんので、これ以上はご回答しかねます(他の方に有用な情報が提供できそうな場合はするかもしれませんが)。2ちゃんねるへの書き込みも私ではありません。ご了承ください。

 

@Lambadaは、前エントリで示したとおり、当時のTwitter名もつけると、

 らくだの卯之助 (@camloeba) 

 

プロフェッショナルな関数プログラマと紹介されているが、2chの僕を標的にした誹謗中傷スレッドで活躍していた。

さて、すでに前半で概論は説明したが、さらに踏み込んでいかにこいつらが間違っているのか解説していこう。

 

Data.now() は引数としては何も渡していないのに、毎回異なった値が返ってくるんですよね。これのどこが参照透明なんですか?

(私訳:「today()は参照透明ではありません。評価して値(例えば"Jan 1, 2001")で置き換えると、明日に実行するのと同じ結果が得られないからです。これはtoday()が状態(時間)に依存しているためです。」)

 

ここで、かろうじてまともなことを書いているのが

これはtoday()が状態(時間)に依存しているためです。

だ。

著作では、

22.5. 関数そして二項演算子は暗黙に時間依存してはならない 副作用がない純粋な関数(Pure function状態を持たない純粋な関数 というのが、より一般的な言い方ですが、いろいろ解説を読んでも、最終的には要するに.、「時間依存しない」という事実を、「時間」というキーワードを避けながら説明されています。「時間依存しない」というのは、つまりImmutableである、いうことです。 純粋、副作用、それから参照透過性というバズワードの問題点は、たとえばこのコードは純粋と言えるのか?これは副作用と言えるのか?と、時間依存の扱いという本質とははずれたところで言葉の定義議論という本末転倒で不毛な議論に必ず陥るからです。 いわゆる「純粋な関数」とは、厳密にその関数の引数のみに依存していると一応の説明はされます。 関数型コードの式を組み立てていくときは、関数そして二項演算子()を活用して依存グラフを構成します。 では、この関数はどうか?

const x = 5;const f = a => a + x;

「純粋な関数」とは、厳密にその関数の引数のみに依存していると考えたとき、この f 関数は関数の引数であるaのみに依存しておらず外部変数であるxにも依存しているだろう?という疑念が出てきて当然です。 しかし、このxは「定数」で、時間変化せず、常に5と等しく、安全に置き換え可能なので、時間依存していません。Immutableです。 関数の別の表記である二項演算子、二項演算についても同様です。演算は左右の被演算子(Operator)2つのみに依存して、依存グラフを構成しなければいけません。 幸いなことに、二項演算の場合は、命令型プログラミングでは当たり前のようにやっている時間依存する演算みたいなことは当たり前ではなく、小学校の算数の教育からずっと、「計算」したらそれで値が決まる、という意識は強く、計算する時間によって答えがコロコロと変わる、というのはむしろ珍しい現象におもえることでしょう。 つまり、我々は義務教育の頃には、演算というものは時間依存しないものである、という正しい作法を徹底的に叩き込まれており、その次に、命令型プログラミングなるもので、コードに出てくる関数では、演算する時間、タイミングによって、値がコロコロと変化する、時間依存するのが当たり前である、という間違った作法を再教育されているわけです。今一度小学校の算数の頃の意識に戻り、演算というものは時間依存しないものである、と頭をリセットしなおす必要があります。 コードの論理的な依存グラフより外の外部と接続しているIOが副作用があり純粋ではない、と言われる理由は、コードの外の現実世界の「時間」に依存しているからです。現実世界は壮大な時間依存するグラフです。 そして現実世界はImmutableな宇宙なので、別に依存してもかまわないですし、ユーザからの入力やコンソールへの出力をはじめIOでは実際に依存する必要があります。 しかしそのときは、暗黙にやってはいけません 22.6. 関数そして二項演算子が時間依存するときは明示的に依存グラフを構成する

 

(引用終わり)

に該当する。

 

ここで、Date.now()

というのは、時間に「明示的に依存している」のは明白です。

これはtoday()が状態(時間)に依存しているためです。

とも書かれているだろう。

【参照透過】という無意味なBuzzワードが、言ってることは結局の所、

式の構成要素がすべて同じなら、式の値は常に同じになるということ。

なんてことは、数学ではアタリマエのことじゃない(笑

ってことでしかないので、

nowだのtodayという、時間依存の関数であると明示されている場合には、数学的な矛盾など起きない。

なぜならばそれは、

常にユーザの現在時間を指し示すインデックスであるNowをもって、

Time(Now)という関数に概念的に一致するからだ。

これで命令型で起きるような不整合があれば、示せばいいだろうが、そんなことは彼らにはできないだろう。

Date.now() がユーザーの現在時刻を元に値を返すなら、それは Date.now() がユーザーの現在時刻を「参照」しているだけではないのですか?

そうだよ、だから?

その論理ならこの、わけのわからん造語である「参照透明」のスペックは満たしてるだろう?

「(私訳:「today()は参照透明ではありません。」というのは、単にその論文の主張が間違っている。

せいぜい僕の主張よりも権威は認められる、と主張するのは自由だが、学問ってそういうもんじゃないよね?Wikipediaからリンクされてる、うん、それで?(笑

だいたいこの輩は、あとから引用するけど、オリジナルから捻じ曲げられた「参照透明」という言葉の経緯はわかって書いてるのか?

こいつがいうところの「参照透明」ってのはどういう意味で使ってるんだ?こいつのオレオレ定義か?どの意味の「参照透明」?そんな定義すらろくにされていない用語はまともな論文として通ってるのか?アホらしい。

逆に(時間にせよ何にせよ)プログラム上に存在しない暗黙の引数を考えれば参照透明などと言い出したら、状態に依存したどのような関数も参照透明になってしまい「参照透明」という概念の存在意義がなくなってしまいますので、そのような定義があり得るとは思いません。

実際に

Now

とか

Today

が、それぞれ引数として

Time(Now)

Time(Today)

というように暗黙じゃなくて明示した現在時間のインデックス値として引数にしてやっても、そうじゃなくても、どっちでもどうでもいいとおもうけど、いずれにせよ、

これ、時間依存の関数だって、明示されているのと一緒だし、実際そういう意味だってコンセンサスはあるわけだよね?(苦笑

 

そして、こういうくだらん、無意味な議論を引き起こすことこそ、この参照透明、参照透過というくだらない造語が害悪であるという証左。

「参照透明」という概念の存在意義がなくなってしまいますので、そのような定義があり得るとは思いません。

うん、そのとおり、非常にくだらないし、こんな「概念」の存在意義なんぞないよ。定義したのがそもそもの間違いで、王様は裸だといえないこういう馬鹿連中を量産している。

 

(編集済み)
 

@Q_Jirou さん、お疲れさまです。
私はこれまでの一連の書き込みを見て、「根本的に議論が成り立たない相手」が存在するという事実に脅威を覚えています(笑)

以前私が書き込んだ「参照透過性」の定義を読んだ後にまだ「Date.now() は参照透過である」との主張を曲げないんですから、毛の人は根本的なところでの言語理解力が乏しいか、私たちの言語体系とは違った世界の住人なのだと思うことにしました。

相手が人でない場合、「人の道」を説くの無意味だと思います。残念ながら…。

 

@totemring
あなたはこれまでのコメントの中で何度も「コンセンサス」という言葉を使ってきていますが、あなたの「Date.now() は参照透過である」という主張はどの程度のコンセンサスが得られているのですか?
もちろん、哲学や意味論や物理の世界ではなく、我々が議論している「関数型プログラミング」の世界での話ですよ。

あなたがこれほど主張しているのですから、「関数型プログラミング」に関する学術論文の中に「Date.now() は参照透過である」と書かれている論文がさぞや沢山あるのでしょう。そのうち 4〜5 編でいいので題名と著者を教えてください。

「そんなのは自分たちで調べればいいだろう」などという『悪魔の証明』を提案しないでくださいね(笑)

 

 

@totemring さん、コンセンサスの話題はスルーですか?(笑)

上記確認済みのように「値」が言語外にしか存在しない、と意味論的に正しい解釈をしていれば、そんな間違った発想はでるわけがない。

私はあくまでも「関数型プログラミング」の範疇で話をしているので、言語内の値の話をしてますよ。
哲学の話や意味論の話は聞き飽きました。「関数型プログラミング」における「参照透過性」の話をしてください。

私の書き込んでいる「日本語」が理解できますか?

 

理解できるよ。

こいつが「哲学の話や意味論の話」を聞き飽きようが、こいつの主観がどうであれ、

そもそもの「参照透明性」「参照透過性」という厳密な議論や定義がある哲学用語、意味論のはなしを、なんか意味を捻じ曲げて混乱を引き起こすだけのBuzzワードとして「関数型プログラミング」ローカルで、数学のアタリマエの作法を言い表すためだけの目的で狂った形式で輸入された事実は変わらない。

関数型プログラミング」における「参照透過性」の話をしてください。

うん、だからやってるじゃない。

これが馬鹿げた無意味な造語としてプログラミング分野に輸入されたにすぎないって狂った状況を説明しているんだ。

おまえが納得しようがしまいが、経緯と事実と、王様は裸だって言えない愚者が量産されてる狂った現状は変わらない。

 

qiita.com

f:id:stq2050:20211229194447p:plain

関数型言語プログラマたちは、それら命令型言語を「参照透過」とは呼びたがらないでしょうから、そういった複雑な数学的・観念的オブジェクトを「値」であると認めることも嫌がるでしょう。それでいてそれらの状態変換器が、彼らの好むところの文法や「モナド」などというバズワードによって取り込まれるや、「値」と呼ぶことに抵抗がなくなるようです。この姿勢は一貫性に欠くではないかと言わざるを得ません。たとえ彼らの「参照透過」の考え方にある程度の統一性が認められたとしてもです。

歴史を紐解くと、なぜこのような混乱が生じたかが、かすかに見えてきます。

 

つまり、ボトムラインとして、この記事のコメント欄で、わいてる一連の馬鹿どもは、定義からして結構デタラメなバズワードについて曖昧な定見で、さもわかってるように「参照透明」という言葉を使いまわしていた事実は間違いない。

僕は、なんか連中の通報でアクティブなアカウントが凍結されていたので「捨て垢」でとことんこいつら馬鹿の相手をしていたのだが、連中は自分らが寄りかかっていた「定義」がグラグラであることに気づいて、単に反論のための反論で食い下がってきた。

 

f:id:stq2050:20211229194542p:plain

繰り返すけど、関数型プログラミングにおける「参照透過性」というバズワードの意味なんぞ、このエントリの前半で示したとおり、

 

式の構成要素がすべて同じなら、式の値は常に同じになるということ。

数学ではアタリマエのことじゃない(笑

程度の意味しかない。

そして、探究心旺盛なひとが書いた記事のように、こういう人は本当に頭がいいと思うし、こいつら馬鹿どもと異なって、王様は裸だ!と言える人なのだが、オリジナルの参照透過性とは、もっと深い意味論の話であって、そんな意味論はたかだか

式の構成要素がすべて同じなら、式の値は常に同じになるということ。

数学ではアタリマエのこと

という関数型コードのスペックを示すのに、何の関係もないし、必要どころか、害悪でしかない。

 

関数型プログラミング」での「参照透過性」の概念が他の分野とは違うと書かれているのに、どうして総論的な結論が出てくるんですか?

くだらない混乱まみれの用語の自分らの過剰評価、王様が裸であることを認めたくないがために、その混乱ぶりを擁護する、そして

私は「関数型プログラミング」の範囲に限定して「Date.now() が参照透過である」ということを証明してくださいといってるんですが?

くだらん、馬鹿造語をもって、そんな破綻した概念をベースに「証明」なんぞできないんだよ。

せいぜい、

Date.now()っていうのは時間依存の関数であることは誰でも知っていて、

DateTime(Now)としでもするならば、時間依存の引数があると整合的に解釈できるし、

 

逆に(時間にせよ何にせよ)プログラム上に存在しない暗黙の引数を考えれば参照透明などと言い出したら、状態に依存したどのような関数も参照透明になってしまい「参照透明」という概念の存在意義がなくなってしまいますので、そのような定義があり得るとは思いません。

とかほざいてるけど、

状態に依存したどのような関数も参照透明になってしまい

なんてことはなくて、

Date.now と today()

なんて時間依存だって字面に書いてる特別な時間関数なんで、

「状態に依存したどのような関数」の話なんてしてないよね?ごまかさないでね?

そして、

「参照透明」という概念の存在意義がなくなってしまいます

 

そのとおりだよ。

式の構成要素がすべて同じなら、式の値は常に同じになるということ。

数学ではアタリマエのこと

の意味しか、オリジナルと違って関数型の世界ではせいぜい、その程度の意味しかないんだから。

Date.now と today()

ってのは、字面で診ても、それぞれの関数定義においても、

時間に依存する関数だ。

今、今日という構成要素がこの数学の式には入ってる。定義としてね。

式の構成要素がすべて同じなら、式の値は常に同じになるということ。

今、今日というユーザの現在値という「引数」に呼応する値は常に同じだよな?

 

HaskellのIOモナドも、全部時間依存の関数というタイプをそろえているだけで、関数合成して、あとで、ランタイムがWorldっていう時間引数を入れるだろ?

それと何が違うの?馬鹿どもが。

 

以上です。

 

Quoraでの「関数型プログラミングが『銀の弾丸』であるという非常識な常識 2022」の書評

2021年末に基本フリーで誰でも読める形で関数型プログラミングの入門書をUPした。構想だけしてたりトライアンドエラーでゼロから書き直したりする時間全部ひっくるめると2年ほどかかった。かなりの長編ではある。

 

関数型プログラミングが『銀の弾丸』であるという非常識な常識2022

 

さて、

 

f:id:stq2050:20211228045355p:plain

magical-programming.quora.com

というのは、僕がQuoraでそこそこ熱心に活動していた頃に、Quora日本語版の運営からの要請を受けて、「Quoraスペース」のアルファ版の一環として僕がオーナーとしてQuora日本語版の最初の最初のプログラミングスペースとして立ち上げたものだ。

 

僕はもう昔から人にものを教える、伝達するという行為が好きで、QuoraというQAのSNSは性に合っていたし、そこそこの認知や評価もいただけていたとは思う。

最終的には、ウイグル人権問題が問題になりだした少なくともSNSレベルでは黎明期に、ひたすら中国共産党の代弁者みたいな中国人と揉めた結果BANされたわけだが、別にそれによって自分の政治的主張というか、単に想像を絶するレベルの人類への犯行についての批判、今、折しも北京五輪ボイコットで問題となっていることや、ましてやプログラミングの考え方が誤っていたからBANされたわけでもなんでもない。

 

このスペースは、なるだけわかりやすく初学者の皆さんへ向けてプログラミングの魅力を伝えられたらなという、自分が教えるのが好きというライフワークの一環として立ち上げたものでもある。

 

Quoraをやっていたころから多少関数型プログラミングに関するお話をしていて、僕の個人スペースなどもフォローしてくださっていた宮西さんという方が、今回の僕の著作を、この僕が立ち上げたプログラミングスペースで好意的に紹介してくださった。

 

magical-programming.quora.com

 

すると、

kissoffunc.quora.com

 

f:id:stq2050:20211228051144p:plain

 

まあ、毎度のパターンです。

必ずこういう輩が「電光石火」の如くネガティブなコメントを投下する。

なんでSNSってこういう人様への人身攻撃についてだけはこれだけ素早く熱心なんでしょうかね?

QuoraでアカウントBAN食らったのは、上述のとおりだし、曲がりになりにも自分で書いてもいる。

note.com

 

その他SNSについては、今回はてなに作ったこのブログエントリでも書いた。

 

ken-okabe.hatenablog.com

ken-okabe.hatenablog.com

 

2chの頃から続いているスレっていうのは、僕は刑事告訴した誹謗中傷スレッドだけど、この人物は、そのスレを半ば肯定的に紹介している時点で、まあしょせんはそういう人だ、というのはわかる。

というか、実際その刑事告訴した誹謗中傷スレに

エナガって名前を見るんだよね。

f:id:stq2050:20211228052247p:plain

jp.quora.com

どんな人物なんだろう?とQuoraのプロフィールを見たけど正体不明。まあ2chの誹謗中傷スレ関係者なんで当然だろう。本名かどうかも怪しい。

もちろん、自分がこの人を調べたらどうか?と訳知り顔に書き込んでるスペースが僕がアルファバージョンの頃に立ち上げたスペースだってことも知らない新参なんだろうし、書き込んでる人が僕のQuora時代からの知友ってことも知らないんだろう。
関数型プログラミングに目覚めた!IQ145の女子高校生の先輩から受けた特訓5日間

については、2015年の仕事だが、ES6以前のコードだし、なるだけ専門用語を使わずに概念だけを物語形式に埋め込んで解説しようとしたけど、用語の正確性についてはたしかに大切だし、自分自身満足した仕事か?と問われると反省点は非常に多かった。

そのためにも今回の著作があるわけである。自身の至らなかった部分も統合的に解説できるようになったし、なによりES6とTypeScriptの恩恵が非常に大きい。

それでも当該Amazon評価では、

f:id:stq2050:20211228053020p:plain

そんなにわるくもない。そこそこの母数でそこそこの賛否両論と言える以上の評価だと思う。この人物がいうように★1レビューが目立つのは、とりもなおさず、こういう電光石火で人様の書いたものや人物を刑事告訴までした反社会的な2chスレすら肯定的に扱うような誹謗中傷好きの人間がアンチ活動に懸命だからだ。

実際に、良心的なレビューでは、

f:id:stq2050:20211228053332p:plain

という感じ。ありがたいことである。

世の中とは、悪意に満ちた人間と、良心的な人間、前者はSNS時代の今、社会問題にもなっているが、こういう実名(かどうか怪しい)顔出し(かどうかもわからない)正体不明の人間でも恥ずかしげもなく一生懸命やっているということなのだろう。

 

QuoraというSNSは僕は政治案件でウイグル人権侵害を正当化する工作員みたいな中国人との喧嘩で最終的にBANされてしまったけど、誹謗中傷という点ではSNSのなかでも非常に気を配っていて安全な場所だと思う。

BNBR(BeNiceBeRespectful)というポリシーがあって、いくらBANされたユーザだろうが、こういう悪意に満ちた評判を貶めるような真似は控えるべき場所だ。

僕がまだ有効なアカウントがあれば、この、末永という正体不明の人物についてはBNBR違反として通報するだろうし、彼のこのろくでもない書き込みについては削除されることは確実である。

 

これに関連して、Quoraが比較的良心的なSNSであることは、そもそも僕が立ち上げたスペースに、知友が親切に紹介コメントをアップしてくれたこともそうだし、末永というろくでもない人物についてはそれなりに反論コメントが自然とつく、ということだろう。世の中悪いことばかりではない、というのが示されるのがQuoraの良心性をうかがわせる。

 

f:id:stq2050:20211228054022p:plain

 

こちらの方は末永氏とちがって正体不明ではなく社会的属性も確認できる。

僕は実際にこういうブログエントリは書くような人間なので、人格批評はご自由に、といったところだが、大枠として、この人は反論を先人切ってやってくれた。その心意気についてはまず感謝申し上げたい。

 

ただし、内容はそれなりに筋は通っており、まったくの間違いという事はないです。

というのは、「まったくの間違い」からの否定と評価を限定的にする以上、また「それなりに筋は通っている」というのは、もし何かおかしいところがあれば、きちんと明示すべきだと考える。

この解説から入って、さらに勉強を深めていく分には何ら問題ない教材と思いますよ。

というのであれば、せいぜい「まったくの間違いという事はない」というような教材は甚だ不十分だと思うし、あえて反論のようなことをすると、

自分が実用性を求めて関数型プログラミングをするにあたり、このレベル以上に「更に勉強を深めていく分」というのはほぼほぼ存在しない。

やるのもちろん自由だが、せいぜい圏論がどうとかの分野で理論的基礎については、これでカバーできるように書いた自負がある。

むしろ、巷で理不尽によく引用されているモナドの説明が、IOモナド専用の説明であることがほとんどである、というかなりデタラメな状況について、ほぼ誰も何も言わないというかなり異常な状況こそが批判されるべきものであって、ここまでギリギリまで本質部分を切り込んだ文献はまず見当たらなかったので自分がはじめて書いたような感覚で、それこそが執筆のモティベーションでもあった。

 

正体不明の末永氏はくいさがる。

f:id:stq2050:20211228054948p:plain

f:id:stq2050:20211228055006p:plain

 

読む価値はあるよ。

というかなんで、どこの馬の骨とかもわからない数学愛好者の中級者の書きなぐりが「指摘」として「正しい」ということに自動的になっており、僕の反論が「読む価値は・・・」みたいに漠然とした印象操作で終わってるんだろうね。

 

しかしよくも全部ネガキャン記事3本もぱっと出してくるものである。

僕は非常に不愉快で面倒くさいけども、「いつものごとくの反論」とか揶揄してるんだけども、こういう人間が近い将来こうやってネガキャン記事を耳揃えてだしてくるだろうと事前に予測していたから、結構面倒くさい気持ちも押し切って、「読者の良識」みたいなものは、こういうネガキャンの前では無力化されるから、著者本人として最低限でも反論を紐付けようとしたんだね。今こうやってこういうQuoraのネガキャンにたいして反論するのも、内心「くだらない」とウンザリしているが、同じようなネガキャン再生産の種として近い将来悪用されるにきまっているのでこうやって年末の朝方の生産性のたかい貴重な時間を割いて書いてるわけ。

 

f:id:stq2050:20211228055538p:plain

僕は、

ken-okabe.hatenablog.com

以下、このTwitterで「ネガキャンやるぞ」と決心した人間について、いかにトンデモかということをそれなりにハッキリとした証拠に基づいて論証したわけだけど、こういう人物については真実なんてどうでもよいのだろうし、なんでこの正体不明の末永という人物がトンデモ中級者による「間違い」の「指摘」とやらが無条件に正しいものとして受け入れているのかは、一切合理的な説明はない。

おそらくこんな人間に、正しい判断なんてできる知見なんて最初から存在しないのだとおもう。

f:id:stq2050:20211228055928p:plain

少なくともここまで僕の著作について間違いだと断定するのならば、そのTweetの指摘がトンデモではなく正しいことを追証すべきだろうとおもう。

 

f:id:stq2050:20211228102604p:plain

文脈より、ここの「反論」とは、ネガキャンやってる中級者によるレビューのことらしい。実際に彼らの僕の著作への論評なんぞ「たいした内容じゃない」し「言いがかりをしたい人が重箱の隅をつついている程度」であるのはそのとおりだ。

まさに、僕がそれこそ各記事のコメント欄で地道にこういうブログエントリのような「反論」をひもづけていることもきっと役立っているだろう。

伝わっているようであれば嬉しいことではある。

 

これに喚起されたのがさらに良心的なコメント。

 

f:id:stq2050:20211228060146p:plain

 

以下、それなりにしっかりとした知見のある方だとわかるし、末永氏のように正体不明でもなく、社会的属性がしっかりと確認できる。

なにより購入していただいた、と著作の価値を認めていただいたことに感謝申し上げたい。どうもありがとうございます。

>圏論に関しては結構丁寧に説明して有るのですが、私的にはモノイドについてもっと言及して欲しかったです。

モノイドについては、圏論の範囲で論証するのは大変面白いのですが、かんたんなことではないのは巷の文献からみても明らかです。

圏論におけるモノイドというのはかんたんではありませんが、実際本書ではそこそこ分量を使ってモノイドの性質についてはあくまで小学校から学ぶ+やらの二項演算の代数学の範囲で説明していますし、それで十二分だとおもっています。

 

>実際モナドの連鎖は同一の型のモナド間でしか行えないのは、このモノイドの特性に由来しているのではと思っています。

これはFunctorの章で説明もしていますが、そのとおりです。

 

>(勉強中なので断言ができるレベルには至っていません。関数合成は受け渡しを行う関数の間で値が一致していればOK)

値ではなく型(Type)の一致です。

関数適用の演算子はパイプラインオペレータで、これはIdentityFunctor=IdentityMonad

であるので、当然型(Type)の一致が必要で、

より複雑な構造をもつ

MapFunctorでも同じように型(Type)の一致、この場合ではArray[] ですが、一致しています。

ただし、IdentityFunctor=Monadと同じレベルで自由に合成はできないので、

flatMapFunctorにすることで、= flatMapMonadで、identityMonad=パイプライン演算 と同等の自由度が獲得されます。

 

>Javascriptでは自然な形でカーリー化ができないので、どうしても無理やり感が否めません。f x y = x + y に f 1 を投入して普通に y を引数とする y + 1 の関数を返す事が出来ません。コードを作成して対応は出来ますが、引数の数が変わる度にコードを書き換える必要が出てきます。それに無限配列が使えないのは意外と地味に不便で、演算開始段階で明確な終了条件が決定出来ない場合のカウンターとして使用できません。λ計算で有名なYコンビネーターも同様にそのままでは実現出来ません。実務で使う場合にはこれらが無くても問題は無いのですが、関数型プログラミングを学習しようとすると物足りない感があります。という事で、しっかりした?関数型プログラミングを学習するには、今一つ感が拭えないのが正直な所です。

 

これについては何が問題とされているのか対象がよくわかりません。

JavaScriptでもHaskell同様にunaryFunctonでのカリー化した扱いは同等にできるので、JavaScriptの自然なカリー化

f x y = x +y

というのは

f  =    x => y =>   x + y

となるだけですね。

おそらくHaskellでは

JavaScriptでいうところの

f(x,y) = が自然と

f x y と暗黙unaryFunctionのカリー化記法ができる、ということをおっしゃってるのでしょうが、この記法の違いはHaskellは簡潔さですぐれいるがJSのアロー記法でもさほど問題なく、本質的な違いは生まれないと思いますが。

 

>関数型プログラミングでは有りませんが、関数型言語の説明は以下のページが自分的には、一番しっくりきたのでアドレスを張っておきます。11. 関数型言語 - プログラミング言語論 ドキュメント

 

web.sfc.keio.ac.jp

 

については、自分は全く評価しません。

これまでこの手の関数型プログラミングの説明を山ほど読んできましたが、それによって、自分が今回著作としてまとめたような概念は手に入らなかったし、具体的にどういうのが関数型コードか、ということがわかったことはありませんでした。

 

たとえば、二項演算を著作では一貫して強調していますが、Haskellモナド則にしろ、あれはすべて二項演算で表記されています。

wiki.haskell.org

f:id:stq2050:20211228062841p:plain

 

圏論でいえば集合の圏の範囲だけが、射は関数に、対象は集合となるので、関数型コードの範囲となるので、モナドは二項演算になるのが当たり前で、二項演算とは、二項関数のことにほかなりません。

 

Haskell界隈でいうところのモナド則とは

オペランド >>= 右オペランド =  値

という二項演算であるのは「誰の目にも明白な事実」なのですが、

モナドが二項演算であることは、まるで説明されていません。

なんででしょうか?僕には意味がわからないですし、誰も文句を言ってるようにも見えません。それどころか、

 

ken-okabe.hatenablog.com

 

で引用したとおり、

FunctorとMonadは二項演算か?

f:id:stq2050:20211228063319p:plain

 

 

誰がどう見ても、Haskellモナド則でも二項演算でしかない、Haskellモナドについて、説明しているのを読んだ後か、読んでもいないのか知らないけれど、

Monadは二項演算か?

などと重ねて疑問を呈してきて何も感じない、平気ぽいのが、このHaskel界隈の現状で、僕はこれ病的なレベルだと思っています。

 

HaskellMonadは二項演算をもちます

間違い

 

HaskellMonadは二項演算(という代数構造)で、

二項演算なのだから当然、二項演算子がある。

というのが正しい。

 

Monadについては「二項演算」だけじゃないだろって感じですが。

とか書いてるのは、この中級者による数学用語の使い方がデタラメで、この中級者は、

「二項演算」という代数構造がなんたるか全く理解できていないのは読めばわかることです。

こんなもんをもちあげる、しかも僕の著述、解説が間違いだと指摘していると公の場で発言できるということは、末永氏のことですが、同レベル以下であることが明白です。

恥ずかしくないのだろうか?というレベルです。

 

しかし実際にこういうトンデモが出回っているのがHaskell関数型界隈の病的な実情で、僕自身は、最初そういう言語界隈を無条件に信用していたので本当にひどい目にあいました。なんでも無いことを理解するのにとんでもない遠回りを強いられました。

同じ苦労を初学者の人々に味合わせるのは忍びないし、自分がわかったならちゃんとわかりやすく解説する義務責任があると一種の使命感をもって著作をかきあげたのです。

その行為については褒められても、

内容はそれなりに筋は通っており、まったくの間違いという事はないです。

この解説から入って、さらに勉強を深めていく分には何ら問題ない教材と思いますよ。

と書かれたとしても、君ら本当にわかってたのか?

わかってたらなんでこんな有様になっている?と疑念を呈さざるを得ません。

実際に、

 

web.sfc.keio.ac.jp

に戻りますが、

参照透過性

 

というのは、別エントリで年内、今日にでも決着をつけるべきテーマなのですが、この概念によって関数型コードがわかるようにはなりません。

 

なぜならば、

1+2=3

とかいう小学校の頃から慣れ親しんでいる二項演算は、二項関数と等価であり、この式こそが関数型コードだ、みたいな説明こそが大事だからです。

 

自分が読んだ本、あるいはこういう持ち上げられている解説ページに、小学校の頃からやってる二項演算が関数型コードの式である、という単純明快な解説がなされているのをこれまで見たことがありません。

 

そして二項演算ってのは、数学の定義上、ここで言われる参照透過性があるわけですが、誰が、これまで二項演算やるのに、参照透過性が重要だ、みたいな教育を受けたことがあるのでしょうか?

1+2=3

という二項演算の式=関数型コードのあり方を理解するためには

「参照透過性」とは完全に不要な用語です。

 

そしてこのページに不要に、それこそデタラメな定義というか定義もされておらず不要に使われている「その時点での状態」あるいは「変わらない」「変更してはいけない

という言葉の使い回しこそが、本書で解説している「時間」に依存するグラフ構造、のことで、こっちのほうを、わけのわからない「参照透明性」をあたかもわかったように解説していることより、解説対象として注目すべきなのですが、解説してるつもりの張本人である書き手がおそらくそんなことは気にもとめていないので、このわけのわからない「参照透過性」という正体不明の用語が再生産されて拡散されています。ありがたがられるように。

11.4. モナド

 

についてもひどいものです。

 

モナドという二項演算の代数構造は、参照透過性という概念とは独立事象です。

たとえばパイプライン演算は、IdentityFunctor=IdentityMonadですが、

1 |> plus(2)  =  3

というのはモナドの二項演算ですが、参照透過性という概念とは関係ありません。

というより、二項演算という代数構造で参照透過じゃないものなんて存在しないのだが、そんな代物をもって、説明しよう、できると思ってる時点でかなり異常な世界なんですね。

 

参照透過?といわれたら、二項演算なんでそりゃ当たり前に参照透過だろう、と言い捨てるしかない、モナドという代数構造について、参照透過という概念でモナドを説明しようとしている。相当頭がおかしい世界なんですね。

で、僕がそういうことを書くと、なんか叩いてくる連中がいる。彼らには合理的な説明ができないのはご覧のとおりです。

当該ページを引用すると、

参照透過性は関数型言語の重要な性質だが、それでは困る場合もある。

  • 乱数を使う場合。 random が毎回同じ値を返しては乱数にならない。

  • 入力を行う場合。例えばキーボードから一行入力する getLine は、人間が入力した値を返すので、呼び出されるたびに違う値になる。

  • 複数の出力を行う場合。例えば putStrLn("1") と putStrLn("2") を評価すると、1と2のどちらが先に出力されるかわからない。

上のような事態に対応するため、Haskellモナド(monad)を導入した

 

これらはIOで、このページの解説でもキレイにすっとばしている「時間」に依存する式のことです。

なんのことはない時間に依存するならば、時間に依存するような数学構造を定義してやればいいだけのことですが、Haskell界隈では、非常にこの辺苦労したみたいで、IOモナドという、IO専用の関数合成の仕組みを導入しましたが、大方は意味なんて理解していないので、こうやって、なぜかモナドという二項演算の解説の冒頭にIOという全く関係ない独立事象をもって紹介するのがHaskell界隈のモナド解説の非常に病的なクセです。

 

そして、流石にまずいと感づいてはいるのか、

  • モナド自体は、データに対して付加的なデータをくっつけた新しい型を作り、その型のデータを複数の関数の間で受け渡す仕組みである。

  • IOモナドは若干特殊で、入出力を行うためのアクションを値とする型である。

などと、とってつけたようなモナド自体の説明をするのだが、こんなもんがモナドの説明になっているわけもありません。

「データに対して付加的なデータをくっつけた新しい型を作り、その型のデータを複数の関数の間で受け渡す仕組み」

 

JavaScriptのMapメソッドの拡張であるflatMapメソッドで確認してみましょう

[1].flatMap(a => [a +1])  // [2]

ではあるが、

「データに対して付加的なデータをくっつけた新しい型を作り、その型のデータを複数の関数の間で受け渡す仕組み」

という言葉で、なんかモナドのことがわかる、という初学者は全世界に誰一人として存在しない、と断言できます。

 

そして、参考資料としてあげられているのが、

 

 

ですが、これ両方とも例にもれずIOモナドのこと「だけ」を書いています。

2個めは表題からも明白ですが、1個目は、

 

m-hiyama.hatenablog.com

 

これは表題として「モナド入門」と決め打ちしています。

関数型界隈のブログ執筆でそこそこ有名な人ですね。結構検索にひっかかります。

気まぐれと偶然となりゆきで、ここ2,3回はモナドを話題にしました。googleで「モナド」を引いてザッと眺めると、「モナドはむずかしいー」とか「モナドで挫折した」みたいな雰囲気が感じられて、説明芸人の血が少し騒ぎましたね。「なら、予備知識ゼロでモナドの説明をしてやろうじゃねーか」と。

タイトルはだいぶ煽っちゃった…… けど、ハッタリじゃないつもり…… けど、実際はどうかな?

 

プログラミングでモナド機構を使うと、関数、値、計算などの概念を一気に拡張することができます。うーん、ステキ。でも、モナドを採り入れているプログラミング言語の例をHaskell以外は知りません(たぶん他にもあるでしょうが)。そもそも、モナドが少しだけポピュラーになったのもHaskellの影響でしょう。

これに関しては正しい部分もあるけど誤解を招くアルアルです。

ただ少なくとも、IOだのピュアだの現実世界だのモナドそのものとは独立事象と絡めてしか説明できないアレな人々よりはかなりまともなアプローチだとは評価できます。

これ、Monadに先立って、絶対に説明しなければならないのは、本書でもMonoidの次に説明したFunctorです。

正確には、

プログラミングでファンクタ(Functor)機構を使うと、関数、値、計算などの概念を一気に拡張することができます。うーん、ステキ。

と書かなければいけません。

本書で解説していますが、Monadの良いところはせいぜいFunctorに二項演算合成してもパイプライン演算(IdentityMonad)のときのように壊れなくなる、あと構造に少しアプローチ可能になるというメリットくらいで、ここで言われている

機構を使うと、関数、値、計算などの概念を一気に拡張することができます。うーん、ステキ。

の旨味は「すべてFunctor」から来るからです。

MonadはEndoFunctorの特殊なケースです、念の為。すべてのMonadはFunctorでもある。

 

●こんな課題を考えてみよう:副作用付き計算

 

モナド概念は非常に普遍的なので、モナドの実例はとんでもなくイッパイあります。モナドの実例をいくつか出されると(例えば、リストと状態遷移と例外と入出力)、それらがモナドという単一の概念でくくれること、共通性を持つことが信じがたいでしょう。

そこで、モナドの実例を1つだけ選んで、その特定事例をシッカリ説明することにします。その実例とは副作用付き計算です。副作用とは、関数、メソッド、手続きなど(計算/処理の単位)が、外部(環境)に影響を及ぼすことです。例えば、ローカル変数以外の変数への値のセット、ファイル入出力、画面への描画などは副作用です。

副作用がなくて、純粋に計算だけを行う処理単位を純関数と呼びましょう。純関数の値(計算結果、戻り値)は、その引数値だけで決定されます。同じ引数を渡すなら、いつでもどこでも何度でも同じ値を返します。それが純関数ってもんです。

 

でました。さっそくIO、副作用の話です。

なんで、この人らは、Monadという二項演算の話を、Functorもすっとばして、いきなりIOという「別の概念の話」しかもプログラミングで扱うのに「時間」のグラフ構造の代数も考えるので、結構面白くもあり非常に大事な話をこうおいう「おまけ」みたいにしてモナドMonad)の説明にしてしまうのか?

 

かなり異常な世界ではありますが、誰も言わない、僕ぐらいがいう、そして本書けば、こういう異常な世界になれてるのか、なんか僕の解説のほうが異端で、先にすすめば「より正確な」IOの説明がある、みたいにゆってくる。

 

ここで繰り返し声を大にして言いたいが、モナドというせいぜい関数型の世界では二項演算という代数構造の具体的な説明をするときに、IOなんていうそれ自体クセがある概念と絡めて説明すべきではない。

 

Haskellで苦労して導入されたIOモナドの歴史があろうが、そもそもあんなもんはIOファンクタであっても結果は同じです。

関数合成ならばファンクタはモナドになるんだから。

 

そしてその後延々とIOモナドの実装の話が続きます。

 

●そして、これがモナド

 

ここらで今までの経緯をまとめて、モナド概念を正式に導入しておきます。

振り返れば我々は; 関数から副作用を取り除き(汚れ作業はCountupMainに押しつけ)、代わりに戻り値に副作用の意図を詰め込み、それによって失われた関数結合の自由さを関数の拡張により取り戻しました。これらの背後には、次のような約束事/手順があります。

 

モナド概念を正式に導入」と書いているので期待感をもって読むが、まだIOの話をしています。関係ないのに。

一般的なモナドも同様で、次の3つで定義されます。

  1. 型Tから新しい型M(T)を創り出す型構成子M
  2. fun:T→M(S)という関数からext(fun):M(T)→M(S)という関数を作り出す関数(高階関数)である(M用の)ext
  3. 型Tの値を、M(T)型のデータにする関数である(M用の)unit

 

なんでに二項演算だと説明しないのだろう?

Functorというベースとなる構造の拡張であると説明しないのだろう?

 

モナド(M, unit, ext)は、3つ組なら何でもいいわけではなくて、モナド法則という法則を満たす必要があります(今は触れません)。

 

いや、モナド法則とは二項演算の式だし、

「今は触れない」のは、それは、僕が本書でやったようなMonoidの説明、左右の単位元の説明をやってないのでできるわけがない。

 

大方、

世界で一番か二番くらいにやさしい「モナド入門」

というのは、こういう、IOという関係ない概念に依存していて、肝心の二項演算の結合性とか左右の単位元とかいう代数的性質については一切語らずに終わります。

 

そして、読むべき参考文献として列挙されています。

いや、こういう解説は僕の本の「先」の位置づけとしては、ないですよ?

と言っておきたい。

フェアに読み比べてみれば良いと思う。

 

あと、僕が読んだ世界で一番悲惨なモナド解説は、

 

qtamaki.hatenablog.com

 

こういうのがありました。

 

あとこういうの

 

ubiteku.oinker.me

 

ふたつとも「世知辛い現実世界」だとか「ピュア、純粋」だとか副作用がどうとか書いています。

 

たとえば、MonadのベースとなるEndoFunctorでは「一切」こんな言葉は使われずに結構正確に説明されていることがほとんどですが、ちょろっといじってMonadに拡張した途端にこういうわけのわからない現実世界と妖精みたいなファンタジーランド、IO一辺倒の世界観になるのは、本当に異常な界隈だと思います。

 

そういうのが普通で、普段こういう輩については黙認していたり本気で勉強になったとおもってるぽい連中が、まあ僕の本については間違っていないみたいな論調でなんか言ってくるのは本当に勘弁してほしいし、これが関数型界隈の病的なあり方である、という事実を確認しておきます。

 

念の為ですが、至って擁護的に長文をもって書評していただいた、そして実際に購入もしていただいた事でも明らかですが、Matsumoto Kazuhisa 氏には上記の僕の考えがあることを添えながらも、今一度感謝申し上げます。

Ohkubo Kohei氏には、まあ個人批判に便乗しながらも一応は問題ないと先陣切ったという点には感謝申し上げます。

宮西さんにはいつも有用なフィードバックもいただいている上にこのように好意的な紹介については感謝いたします。

正体不明の末永という人については、まあこういう人らには本当にうんざりしています。なんら中身がなく悪意があり、教育側面でいえば悪い効果しかもたらしていないでしょう。BNBR違反でもあります。

 

【『関数型プログラミングに目覚めた!』のレビュー(Day-1)】へのレビュー

まず直近の著作としては、2021年末に公開した

 

関数型プログラミングが『銀の弾丸』であるという非常識な常識2022

 

がある。

 

その上で、2015年初に紙の書籍として出版した本へのレビューが

Qiita上にあり、

qiita.com

これは、当時、誹謗中傷、名誉毀損刑事告訴して事件化していた犯人界隈がQiitaの捨てアカウントを使ってコメント欄で議論を茶化す、妨害するという惨憺たる結果となったレビューである。

 

主犯はこいつ。現在も継続している。

image.png

$ analyze @Sacchii-aum
投稿した記事
No data
LGTMした記事
No data
回答した質問
No data

またスパム捨て垢を急遽こしらえて荒らしに来たんだ?

@Sacchii-aum は僕が刑事告訴した誹謗中傷犯 
ちくわ 芦田真一
でQiitaにも捨てアカウントを継続的に作成しながらネットストーキングしている。
https://twitter.com/tikuwa_zero
image.png
 
上のアカウントデータでも明白なとおり、極めて特異な挙動を示しており、本件専用にコメント欄を荒らす目的でQiita上に急遽作成した捨て垢。しょせんこれがQiitaの炎上の正体。

 

 

元のレビュー主は、今回の著作である

関数型プログラミングが『銀の弾丸』であるという非常識な常識2022

につっかかってきた連中

ken-okabe.hatenablog.com

ken-okabe.hatenablog.com

ken-okabe.hatenablog.com

 

ken-okabe.hatenablog.com

ken-okabe.hatenablog.com

ken-okabe.hatenablog.com

 

 

と同類のアレであるが、

自分はQiitaを「その当時の経営陣(今はもう売っぱらって総入れ替わりしている)」からBANをされて、うまく立ち回れなかったので、十分に誤りを指摘して反論する機会に恵まれなかった。

結局は、あろうことか、当時のQiita運営がこういう匿名炎上目的のアカウントの思惑に乗っかる形で、僕の反論コメントを消してしまったので、現状、間違ったネガキャン情報がそのまま残っている。

 

 

とか、今さら来るので、ああ馬鹿相手の沙汰でもちゃんとカタつけておかないと、馬鹿が延々やってくるんだな、と確認したので、本エントリでは一連のネガキャンを是正する。

 

qiita.com

 

まず、この本はES6(ES2015)以前の時代の著作であり、直近の本のように洗練されたことはなにもできていない。

これは当時のJavaScriptの限界であって、その総括もしたくて直近の本を書いたということもある。

2015年当時のその仕事は、現在の基準ではけして満足がいく仕事ではないが、至らない部分に目をつけられて不当なレベルでネガキャンをやられてしまったという不当性は、2022年バージョンの現在に至っても、同じようにネガキャンやられてるところをみると、結局は内容の不備とか、結局連中にはどうでもよくて、実際後で書くけれども、自身のいい加減な主張でも相手が差し出す渡船があれば乗ってしまう茶番とか、内容なんてどうでもよいなだろうなと強く再確認できる。

それに加えて、前述のような刑事告訴した誹謗中傷犯がQiita上で攻撃してきたので、このレビュー記事はグダグダになってしまっている。

 

 

という文句もあるが、今回の2022年の著作でも踏襲した。もっとも適している題材だし、この人物は、

 らくだの卯之助 (@camloeba) 

プロフェッショナルな関数プログラマと紹介されているが、2chの僕を標的にした誹謗中傷スレッドで活躍していた。

プログラマでもなんでもない、橋にも棒にもかからない、じょうきの「ちくわ」はそのスレで、こういう技術的になんか言いいた中級プログラマ連中と一緒になって、個人攻撃をからめてありとあらゆる方策で攻撃を試みていた。

 

素人としては深入りを避けたいと思いますが、そうだとすればそもそもJavaScript選んじゃったのが関数プログラミングへの無理解を象徴的に示すものなのだということになるような気がします。)

 

結局はこれ。

ES6(2015)以降はかなり、こういうものいいは減ってきたものの、2022年になろうとしている今でも、JavaScript関数型プログラミングの相性が悪いというのは、こういう文句垂れの連中の一貫したテーゼだ。

ほぼ全員が同じことを言ってくる。

要するに、僕をコケにしたい裏にはベースとして、連中がそういうHaskellやらMLやらOCamlやら関数型言語と認識されている自分の世界で愉しんでいて、そこで得た考えから、JavaScriptは違う、と信じている。というより、自身の言語選択の好みをスキをみては押し付けてくるという感じか。

 

誤解のないように言っておきたいのだけど、僕は本書でもTC39を批判というか糾弾に近いことをしているし、言語としてJavaScript最高!みたいな信者でもなんでもない。

TypeScript含めて専門ではあるけれども、関数型の入門として、誰がMLの本を読みたいのか?と正直思うし、実際シェアとしてはJavaScriptは圧倒的で、小学生の入門言語としても採用されている実情があるので、その路線でアプローチしたいというのは、2022書籍の冒頭で書いている通りだ。

 

こういうMLやらOCamlの連中は、自己中心的な連中でそういう初学者に誘導という考えなんてない。

そして初学者むけに姿勢をさげたら、その姿勢をさげる、というのを弱みみたいにおもってなおさら攻撃してくるのがかなりタチが悪い。ようするに「ナメてくる」わけである。

「問題の論理」?

これについては、2022年書籍でいうところの「課題の本質」だ。

しかし、たとえば「与えられた配列sをソートせよ」という問題を与えられたらどうするんでしょうか。s.sort(isGreater)と書け、ということなんでしょうか。肝腎のそのsort()はどうやって書けばいいのかは、およそ「問題の論理」からは出てこないように思います。

 

まあそのとおりだね。

「課題の本質」か「課題の解法」が導かれるのではないのは自明のこと。

ここで書いているのは、「課題の本質」ではない制御構文、フローについて余剰の解決をする必要があるということ。

 

どうやら「クールなコード」を書こうとする限り、ソートを行うコードを書くことはできないようです6。とすると、「神の目」はどうやら役立たずだということになるでしょう。

「課題の本質」に即したコードを書こうとする限り、課題の解法は得られないようです。。。とか言ってるのと同じで、まあ単なる揚げ足取り。

ネガキャンやりたいんだなーってのが伝わってくる。

 

「関数という論理操作」?

これについては、まあ揚げ足取りの続き、とみなすこともできるが、ある程度正しい批判はしている。

ただし、関数が写像だ、なんてことはわかっており、あまりにも初心者の日常生活に寄せすぎて、アナロジーがうまくない、ということ。

実際これはありがちなミスであり、モナドをIOモナドの説明と同一化して、妖精がどうちゃらピュアな世界がどうちゃらとか書いてる悲惨な解説があるので、この要素については文句垂れたい気持ちは共有できる。

 

「『まとまり』は美しい単一の論理構造」?

これについても指摘は正しい。

といっても表現の問題ではある。

今らならこれはこんな抽象的なことではなく、Monoidという代数構造である、と変に数学的な概念をさけずに、ストレートに説明すべきだというのはわかるが、当時は、まさに「妖精とピュアな世界とモナド」みたいなアホなアナロジーが機能すると過剰評価していたきらいがある。

 

追記(2015/05/30):デスクトップアプリケーション??

 

これについては、最新のコードは、おおむね、FRP

kentutorialbook.github.io

として公開している。

 

FRPでなければならない。

そのことは、

 

状態機械の数学的構成では状態遷移が状態と入力から状態への関数として表現されるというだけのことではあり(状態渡し)、状態遷移が複雑になれば状態遷移を純粋な関数として表現する作業も複雑になり困難になるので(そしてそれはしばしば綺麗な関数合成では上手くいかないようなものになることが多いので)

と書いてる本人も理解しているようだ。しかし、

結局のところ少なくとも現状のFRPも(イベントのシグナルから状態のシグナルを構成する際に状態遷移をそうした関数で表現する必要が出てくるので)本質的には銀の弾丸にはならないと言わなければなりません(関数プログラミングで書けるということの恩恵はもちろんあるとしても)。

というのは、間違いで、

(イベントのシグナルから状態のシグナルを構成する際に状態遷移をそうした関数で表現する必要が出てくるので)

となにかわけのわからない共通項をデッチ上げて有効性を否定するポーズは見せているがなんのことやらわからない、というのが事実。

 

ほかにも、著者が本書の執筆期間にStackOverflowというサイトに投稿した質問reducemapで定義するにはどうしたらいいかという質問です)を見れば、著者がmapreduceについて何も理解しないままに本書を執筆していたことは明らかです。

これは、2chででっち上げられたデマであり、このQiita記事を書いている人間が、その2chスレの住人であることを確認できた事案であった。

これは、直近の2022書籍でいうと、mapFunctorの挙動で、MonoidのFoldを表現することが可能か?ということを考えており、reduceからmapが構成される、なんてことは百も承知である。

StackOverflowの回答者の不遜さは当時特にひどかったので、その不遜さも、あたかも僕がコケにされたという印象を連中に利用されることになっていた。

誹謗中傷犯が2ch界隈でわーっとなっているタイミングでは「良い攻撃材料を見つけた」ということで頭がいっぱいになっているわけで、所詮この書き手も、ネガキャンやりたいのでその事実を最大限に拡大しようとしていることが見て取れる。

実際にmapではなくてモナドであるflatMapでは配列構造にアプローチできて、要素の数を増やすということができるので、当時、では要素の数を減らすことができるのか?と手っ取り早く確認するためにSOで質問したにすぎないのだが、回答者は馬鹿にするだけのアレだったし、その結果を悪用されたにすぎない。

従って、

逆にreduceを用いてmapをどう定義すればいいかも関数プログラミングの勉強を始めたその初っ端に出会う練習問題に過ぎないような本当に基本的な話題に過ぎません。このような著者に対して関数プログラミングに関して技術的になにか意味のある「反論」を期待することはできず、つまるところ本書と著者についてはもはやこれ以上なにをしようもないのです。

というのは、誹謗中傷に過ぎない。

 

次のコメント

@Lambada

だが、こいつが、

 らくだの卯之助 (@camloeba) 

による捨てアカウントである。

逆に、岡部氏のコードはよく見ると state = x; という部分で
カウンタ変数への破壊的代入を行っており、何ら関数的ではありません。
(無駄に複雑なスパゲッティ・プログラムですが、よく読むと要するに
state = state + 1; と同じ破壊的代入です。)

 

これについては、2022年書籍についても、まったく同様で「破壊的代入」を使っている。

その正当性はこういう中級者除けのためにもうちょっとうまく説明できていて、22.4. GitはImmutableな永続データ構造Persistent data structure

からはじまり、

23.5. ReactiveFunctor のタイプコンストラクタ(型構築子)まずはタイプコンストラクタですが、mapFunctorの場合は、 a から [a]というように大変シンプルでした。 ReactiveFunctorのタイプコンストラクタであるIOはいったいどういう構造になるのでしょうか? 要するに表計算ソフトのセルの実装そのものです。セルの挙動を考えると、

Aには、無限リストとして、[f, g, h, ..........]のように登録された関数が追加されていて

となっていて、素朴な感覚としては、時間軸で刻々と変化する可変長の無限リストの構造です。つまり、永続データ構造Persistent data structure) 

永続データ構造(えいぞくデータこうぞう、英: Persistent data structure)は、変更される際に変更前のバージョンを常に保持するデータ構造である。このようなデータ構造は、更新の際に元のデータ構造を書き換えるのではなく、新たなデータ構造を生成すると考えられ、イミュータブルなデータ構造の構築に利用可能である。

ですね。 ただし実際に実装するとなると、これは履歴を全部保持するようなバージョニングのデータ構造にする合理性はあるのか?という強い疑念がでてきます。 つまり、表計算ソフトの操作履歴をGitのように全バージョンを保持しておく必要がどこにあるのだろう?ということです。 普通に考えてその必要はないので、最新の履歴、つまり直近の値だけを保持することにします。なんのことはない、Mutableなデータですが、あくまで概念的にはImmutableで最新、直近の値にアクセスしている、ということにします。 Mutableな値で問題となるのは、根本的に依存グラフを構成していないからです。依存グラフで「管理」もされていない値が時間軸で変化していくのは非常に良くないですが、最新の値が常に依存グラフの一部である場合は、なんの問題も発生しない、ということになります。 そしておそらくこういうトリックを使うのは、こういうIOのFunctorを実装するときに限られるでしょう。なぜならば、これ以降で同じことをしたい場合は、このIOのFunctorがすでに手元にあるので、それを使えば良くて、それでImmutableなデータ構造とみなせるからです。

 

 

(引用終わり)

 

ということです。

FRPの実装では、「必ず」いずれかの時点で破壊的代入が行われています。

その理由については本書の

22. 関数型リアクティブプログラミング(FRP) 時間に依存する関数型コードの書き方

を読めばわかるでしょう。

もっというとこれでわかると期待したいのですが、もしわからないのであれば、加筆対象になりえます。

岡部氏は

関数型プログラミングパラダイム内で、そういうマウスボタンが押されているのか、押されていないのか?入力の状態が時間遷移していく場合、上記ブログエントリでも論じたSICPでも、拙書でも解説しているとおり、FRPの実装が必ず必要となります。
「時間」が本質的だから、時間をを集合、SICPの言葉でいえば「遅延ストリーム」とした実装が必要です。』

と断言しているにも関わらず、現に私やnonstarter氏のコードのように
「時間」も「ストリーム」も出てこない関数的状態渡しによる実装が
容易である以上、岡部氏の誤りは明らかだと思います。ご参考まで。

基本的に、このOCamlのらくだなんとかは、FRPのなんたるかは勉強していないことが伝わってきます。

継続渡しなんぞ誤魔化しのテクニックでは、複雑な関数合成などでは通用しないとのはこのQiita記事書いてる人間もわかってることで、応用範囲の広いフレームワークはあくまでFRPです。

 

(編集済み)
 

hiyakashi_さん

著者がFRPを実施しているとは言えないと私は考えています(ブログに掲出されたクリックカウンターのコードでカウンター用の変数を破壊的に更新していることに鑑みても)。foldpについては、以前に私がElmを試した際にfoldpに渡す関数がやたらと複雑化・肥大化してしまったことの印象が強いせいもあるのでしょう。私の扱い方がヘタクソだっただけかもしれません。私はシグナルのもっとも重要な用途が状態の保持であると思っているので、先行する状態に依存する状態を与えるために必要なfoldp(やその相当物)がFRPにとって非本質的だとは思っていません。

ともあれ、ご指摘に対応して本文の該当箇所には手を加えました。

Lambadaさん

ご指摘についてはその通りかと思います。

 

まあ、なんかかなり適当なことを書いていますね。

2022の書籍では最後の方でReactiveFunctorからのMonad化と精緻に解説している。

基本的な考え方は2015年当時となんら変わっていない。

こいつらが、「ご指摘についてはその通りかと思います。」とかやってるのはネガキャンやりたいばっかりの茶番である。

たとえば、最初は

状態機械の数学的構成では状態遷移が状態と入力から状態への関数として表現されるというだけのことではあり(状態渡し)、状態遷移が複雑になれば状態遷移を純粋な関数として表現する作業も複雑になり困難になるので(そしてそれはしばしば綺麗な関数合成では上手くいかないようなものになることが多いので)

状態渡しでは複雑な応用局面になると困難になる、とわかっていた言及をしていたのに、FRPを否定したいばっかりに、

現に私やnonstarter氏のコードのように
「時間」も「ストリーム」も出てこない関数的状態渡しによる実装が
容易である以上、岡部氏の誤りは明らかだと思います。ご参考まで。

となんかネガキャン材料を渡されたら、

「ご指摘についてはその通りかと思います。」

と、ころっと立場を買えてしまったw

 

再度断言しておく。

状態渡しなんぞではできることは限られている。

FRPではすべての局面で利用ができる。

 

とりあえず現時点でUPしておく。

 

あー最後に。

参照透過性については別エントリでやる。

あと、

OCamlネガキャンやってる連中。

特に、状態渡しでFRP不要説をぶっこいている連中は、

本書(2022)で、

関数型プログラミングが『銀の弾丸』であるという非常識な常識2022

23. シンプルでミニマルかつ強力なFRPの実装 23.1. Demo

Reactive programmingWikipedia項目の前半部分に、FRPのDemoにちょうど良さそうなコードがあるので、それを活用します。コードを引用すると、

var b = 1var c = 2var a = b + cb = 10console.log(a) // 3 (not 12 because "=" is not a reactive assignment operator)
前半は普通の命令型のコードで、一旦、
var a = b + c
と定義というより「代入」したあとで、
b = 10
と、bを「破壊的代入」しても、a の値についてはすでに計算済みなので影響を受けません、という、まずまず命令型プログラマにとっては、常に考える対象となる事柄で、特に工夫のない関数型コードであっても、ある程度共有できるアイデアが説明されています。その後で、長いコメントがあり、
// now imagine you have a special operator "$=" that changes the value of a variable (executes code on the right side of the operator and assigns result to left side variable) not only when explicitly initialized, but also when referenced variables (on the right side of the operator) are changed
ここで、明示的に初期化されたときだけでなく、(演算子の右側で)参照される変数が変更されたときにも、変数の値を変更する(演算子の右側でコードを実行し、その結果を左側の変数に代入する)特殊な演算子「$=」があるとします。
と仮想的なリアクティブ演算子があるという説明があり、その後、その仮想リアクティブ演算子を利用したリアクティブプログラミングのコードがあります。
var b = 1var c = 2var a $= b + cb = 10console.log(a) // 12
まずまずリアクティブというアイデアは理解しやすい、特に命令型コードに慣れ親しんでいるプログラマにとっては、コードの上下のフローが逆行するようなありえないことが実現しているよう見えます。 この仮想的なリアクティブなコードを、今から実装するFRPの実際のコードに書き直します。
const b = IO(1);const c = IO(2); const a = allNoResetIO([b, c]) ['>='](([b, c]) => b + c); b['>'](next(10));const Log = a['>='](log); // 12b['>'](next(100)); // 102
12102
オリジナルのコードのとおり、普通に
console.log(a) // 12
と書いてしまったら、さすがにそれ以上どうしようもないので、せっかくなのでLog自体もリアクティブに紐づけています。
const Log = a['>='](log);
こういう式を定義しておくことで、Logは恒久的にリアクティブ値である a にリアクティブに反応してlogを書き出し続けます。従って、この式の後で、
b['>'](next(100));
と、これもリアクティブ値である b を更新すると、リアクティブに 102 とコンソールに書き出します。

(引用終わり)

 

このコードは、そのOCamlの継続渡し(こっちが言ったんじゃない、かんたんに実装できるからFRPはいらんみたいなことをそちらが主張したんだ)でどうなるか、やってみて?

もしクリアしたら次の課題もだすから。

 

 

型はオブジェクト(対象)であり集合の圏なら集合 カリー=ハワード=ランベック(同型)対応

b.hatena.ne.jp

 

「関数型プログラミングが『銀の弾丸』であるという非常識な常識2022」の感想 - Qiita

それな感。型はカリー・ハワード対応等で集合の様にも使えるけど、集合そのものではないよな。あとあの記事の理屈なんか全体的に対象がモノイドである事が普遍的な前提みたいなものを感じるんよな。何なんだろうアレ

2021/12/16 15:24

b.hatena.ne.jp

 

去年の年末に本書の超アルファ版をUPしたときに、冒頭に

ja.wikipedia.org

のことを書いた。

まさに冒頭に結構なウェイトをもって書いたので、すこぶる評判が悪かった。

結局の所、こういう関数型の本を書くときには、目線は初学者に向いていなければならないという根本的な方針を徹底することとした。

なぜならば、自分自身、これまでの経験として、ほんとうに鬱陶しい障害となるのは、こういうちょっとわかった風の中級者連中であり、人様の本への論評にかこつけて、なんか偉そうにドヤって、あたかも著述の内容が正確ではない、というような風評を流すことがあるので、彼ら除けの「お札」みたいに権威を貼り付ける、という最低限の作法だけにしようと思ったからだ。

 

それが、「型は集合」の章であり、このブログでもなんどもしつこいように貼り付けているnLabの項目だ。

ここでもう一度繰り返そう。ここで二つの言い分がある。

ひとつは、どこの馬の骨ともわからない中級者が匿名アカウントでハテなブックマークに殴り書きしたようなコメント

 

それな感。型はカリー・ハワード対応等で集合の様にも使えるけど、集合そのものではないよな。

 

もう一方は、数学の引用文献としてそこそこ権威がある

nLab記事から

nLab は、数学・物理学・哲学の研究レベルの内容について扱ったウィキである。nLabはMathOverflowにおいて、質問前にチェックするべき標準的なオンラインの数学文献の1つとしてリストされている。多くの質問と解答が、nLabを背景資料として用いている。また、nLabはバイエズがアメリカ数学会American Mathematical Society)に投稿した数学ブログのレビュー記事の中で言及されている2つのウィキのうちの1つである。

Type theory versus set theory型理論 vs 集合論

Alternately, we could change our terminology so that what we have been calling “types” are instead called “sets”.あるいは、これまで「型」と呼んでいたものを「集合」と呼ぶように用語を変更することもできます。 Thus, words like “type” and “set” and “class” are really quite fungible. This sort of level-switch is especially important when we want to study the mathematics of type theory,このように、「型」や「集合」や「クラス」などの言葉は、実はかなり代替可能です。このようなレベルの切り替えは、型理論の数学を研究するときには特に重要です。

 

「集合そのものではないよな。」という漠然とした言及が、極めて具体的に「初学者」むけではない解説記事に詳細があるのに、なんでそれを上書きできると思ったのかよくわからない。

 

おそらく、

型はカリー・ハワード対応等で集合の様にも使える

というハッタリ文句が効くと思ったぽいのは想像にかたくない。

本書の前バージョンから、この「カリー・ハワード対応」というお札を引っ込めた途端に、その分だけ、なんか言ってくるんだな、ほんとうに鬱陶しいと毎度実感する。

 

今回、おそらく「チューリングマシン」だとか「計算理論」とか、グラフ理論のところで、「関係がある」とだけ書いたら、それについてはなーんもイチャモンつけてこない。

 

モナド圏論については、それについて集合の圏では二項演算である、とまさにそれがせいぜい、HaskellMonad則のページで書かれている全部の範囲なわけだが、そいつはその演算子の定義のコードを自分で出しておきながら、いきなり集合の圏に限らないモナドの定義の話をしだした。

結局の所、くだらない連中には、手間がかかってもお札を貼り漏らすことがあっては行けないということを再度確認しなければいけない。

 

型はカリー・ハワード対応等で集合の様にも使える

 

という言及に、こいつは、nLab引用した部分の言及以上にいったい何の価値を感じているのだろうか?


9.4. スーパーイディオム分野にまたがって、基本、同じ概念を違う用語で表現しています。

型理論Type theory 型(type) 関数(function)/ 写像(map)
集合論Set theory 集合(set)始集合(domain)と終集合(codomain) 写像(map)
解析学Analysis 定義域(domain)と値域(range)と終域(codomain) 関数(function)
代数学Algebra 集合(set)/ 被演算子(operand) 演算子(operator)
オブジェクト指向Object-oriented programming オブジェクト(object) メソッド(method)
圏論Category theory 対象(object)/ 圏(category) 射(morphism)

情報処理・プログラミング データ(data) 処理(operation)
このなかで、最も根底となる、すべての概念の包括的な枠組みが圏論Category theory)で、これまで永らく数学の基礎であった集合論でさえも圏論の言葉で再定義されます(集合の圏category of sets)における射(morphism)関数(function)になる)。 型(Type)とは集合Set)のこと。関数(Function)演算子(Operator)は本当は同じもの。オブジェクト指向のオブジェクトは、数学の集合(Sets)に相当し、二項演算の左側演算子(Operand)になります。オブジェクト指向のメソッドは、数学の二項演算子(Binary operatior)に相当しており、オブジェクト指向のメソッドの引数は、二項演算の右側演算子(Operand)になります。 と既に明確にしたとおり、本稿の方針は、二項演算子(Binary operatior)を最大限に活用することなので、このなかでどの分野の言葉をもってコードを書いていくのか?となると、当然、代数学Algebraになります。

 

と書いているのだが、ここで

このなかで、最も根底となる、すべての概念の包括的な枠組みが圏論Category theory)で、これまで永らく数学の基礎であった集合論でさえも圏論の言葉で再定義されます(集合の圏category of sets)における射(morphism)関数(function)になる)。

と、まず基底は、圏は集合であり、射は関数となる、集合の圏が基底であると示している。ただし、こんなもんは、イチャモンつけたいこういうごく一部の、まあどうせろくに読みもしない読者対象から外したい連中しか気にもとめないだろう。

いちおう初学者向けに将来にむけて、という意味は断固あるものの、半分くらいはこういう連中除けのお札であることは否めない。

上記の図では、型はオブジェクトに対応している。

これは、ある程度の偶然もあるが、

対象(object)/ 圏(category)

のオブジェクトにも対応している。集合の圏では、集合だ。

カリー=ハワード同型対応 - Wikipedia

カリー=ハワード=ランベック対応

において、

このカリー=ハワード=ランベック対応は直観主義論理、型付きラムダ計算およびデカルト閉圏との間の対応として知られる。ここでは

オブジェクトは型

あるいは命題に、モルフィズムは項あるいは証明に解釈される。

と書かれているそのものである。

 

元のQiitaのネガキャン記事では、型が集合ではない、とおもうという主張を、このブログ記事でも糞味噌に批判したが、TypeScriptの実装が、とかかなりトンデモな理路でやらかしているわけで、そういうトンデモにどのように「それな」と同調できるのか意味なんてわかるわけもない、まあこの人物がトンデモだという説明しか成り立たないが、せいぜい

型はカリー・ハワード対応等で集合の様にも使える

と、ボサーっとした意味不明なnLabの引用文を上書きするだけの論旨があるわけもない、

集合そのものではないよな。

ってのは、メタな観点ではこのような有用な観点で同一視されているものを、メタではない観点では、そりゃ

型理論

集合論

は、繰り返すとここで論じているメタな観点の同一視ではないレベルでは「そのものではない」のは自明なので、こういう主張の仕方するやつらって世の中で消えることはないけど、有害だよなあ、と思った次第。

 

基本的に、僕の書いたもんに文句つけてくる人間っていうのは、一様になんか思い込みが激しくて、最後には、自分の好きなPureScriptとかうAltJS使えとか、ML使えとか、自分の趣味領域の好みの発露をしてそれで満足みたいな、議論には適正のない、ましてや書評とかやってほしくない単に自己中心的な人種ばっかりで、迷惑だなあと思う。

 

 

 

JavaScriptには関数の末尾再帰最適化がないので関数型には不向きだというデマ

冒頭報告

 

当方を不当に侮辱、誹謗中傷した人間から謝罪文が掲載されている。

 

nantonaku-shiawase.hatenablog.com

 

本論

 

anond.hatelabo.jp

関数型プログラミングが『銀の弾丸』であるという非常識な常識2022のなにがダメなのかわからない人が多いようなので、個人攻撃をまったくせずにダメ出しする。

まず言っておくが、私はあの記事ほとんど読んでいない。しかし、簡単ダメ出しできる。

 

また読まずにダメ出しするというアレが出てきた。

しかも、また致命的なレベルで勉強不足の分際で「しかし、簡単にダメ出しできる」とか宣っている。

こういう変な界隈の連中は非常に思い込みが激しいのと同時に、まともに勉強なんてしていないので、とんでもない勘違いワールド世界の住人である自覚もないのだが、ひとりで趣味でやっている分には「勝手にしろ」とか思うが、いざそれをこういう形で喧伝したり、ましてや他人様の著作にミソをつけるのが動機でやられたなら、こっちは黙っては居ない。

これまでこのパターンのアレな連中にさんざんこの類型の不条理なネガキャンをやられてきたし、問題は、それが間違ってると知ってる連中は黙ったままだし、問題のネガキャン当事者以下のアレはそろって、同意する、とかいって、あろうことかそのデマに「信憑性」のようなものを与えてしまう。非常に悪質である。

 

あの記事はめちゃくちゃ長いのに末尾再帰に触れていない

あの記事急所は末尾再帰である

記事内を「末尾再帰」で検索してみよう。1か所もヒットしない。「末尾」でも1か所もヒットしない。そう、あの記事はめちゃくちゃ長いのに末尾再帰に触れていないのである。では「再帰」ならどうだろう。11か所ヒットした。しかし、具体的な再帰コードはまったくない。長い記事内にあれだけ多数のコードを書いているにも関わらずである

 

再帰については、まさに冒頭のreduceそのものに帰着して説明は完了しているので、特に必要ないかな、とは思ったが、ああやっぱ馬鹿な中級者はこういう角度から勘違いsて攻撃してくるんだ、とわかったので、このエントリ終わったら加筆に取り掛かってやろうとは思う。

 

末尾再帰重要

「末尾再帰って何?」とか「再帰ってそんな重要なの?」と思う読者も多いだろうから、末尾再帰重要さだけ説明しよう。

あの記事は、forやwhileを使わないプログラミング手法を前提に書かれている。記事内を「制御」とかで検索すればわかる。

末尾再帰はforやwhileの代わりになるもので、そういったプログラミング手法には欠かせない。forもwhileも末尾再帰も使わないとなると、ツリー探索などのアルゴリズムを書くことが困難になる。(こういったことが苦手な私に思いつく他の方法は、setIntervalを無理やりforループの代わりにするくらい)

 

「あの記事は 前提に書かれている。」

とか読んでもないやつがねー(苦笑

for や whileは、だから記事に書いているとおり、Fold/Reduceになるんだよ(ばーか)というか読めよ?
と言ったところだろうか。

というかなんでこいつら「読んでない」ということを誇らしげに言いながらこういうトンチンカンなこと言って生きてて平気なのだろうか?
異常者だと思う。

 

JavaScriptはたいてい末尾再帰サポートしていない

そもそもほとんどのJavaScript実行環境は、末尾再帰サポートしていない。つまりJavaScriptはforやwhileを使わずに込み入ったプログラムをまともに書けるような言語ではない。あの記事に書いてあるようなことをする言語ではないのである。私は別にそれでもいいのでTypeScript使いまくってるけど。classとか好きだし。

あの記事JavaScriptを使っている理由は、JavaScriptが人気だからだろうか?もしそうだとしてもダメである。あの記事は「JavaScriptは、ほどんどの実行環境が末尾再帰サポートしていない、このプログラミング手法に適していない言語である」といったこ自体に触れていない。人気のある言語を使いたいなら、他の末尾再帰サポートしている人気言語を使えばいい。

末尾再帰をサポートしていない、そのとおりだ。

そんなもんなくても関数型コード書けるし、末尾再帰が必要なコードっていうのは、理論的にはラムダ計算からの再帰ループを実装することでチューリング完全性を示したり、数学の漸化式の数列をそのまま実装することで理論的にはみるところはあるが、リアルなコードでそれをそのまま書くのは「アホ」だ。

これは、リアルなコードでYコンビネータをもってFold/Reduce式を書く人間がいないのと同じ意味で「アホ」なのだが、

Factorial(階乗計算)やらFibonacci数列のコードでは再帰の概念の説明に役立つのでサンプロコードとして示されていることは多くて、特にHaskellに足突っ込んでる系の中途半端に関数型プログラミングを知ってる「つもり」の中級者がひたすらそのアホなコードを再生産していて、以上の引用文のようなアホなデマを流していて甚だ有害である。

 

あの記事は「JavaScriptは、ほどんどの実行環境が末尾再帰サポートしていない、このプログラミング手法に適していない言語である」といったこ自体に触れていない。

触れてないことなんて山ほどあるし、解説の流れの優先度ってもんがあるからね。

JSが末尾再帰をサポートしていない、なんてまったく重要な事柄ではないし、このプログラミング手法=関数型に適していない言語である、っていうのは、こういう勉強不足で知識不足の馬鹿連中の間違った知識にもとづくデマでしかない。

しかし、こういうデマを流して、しかも賛同する同類がいるとわかった以上、なるだけ早く触れざるをえないだろうということはわかった。非常に厄介である。

f:id:stq2050:20211220061934p:plain

f:id:stq2050:20211220061857p:plain

f:id:stq2050:20211220062033p:plain

 

 

以下、すでにこいつの返信は一部スパム認定されて見られないのでこちらの返信だけ引用する。しかしそれで十分である。

-----

 

 

まあたしかにそうだな。Haskell界隈の関数型の解説はかなり筋が悪い。永遠に、理路整然としてものが続くのは当たり前なんで、自分で理路整然としてものを書いたら、こういう馬鹿が読みもしないでネガキャンの挑戦してくるわけだ。

 

ちなみに見せた記事はこれ

en.wikipedia.org

In functional programmingfold (also termed reduceaccumulateaggregatecompress, or inject) refers to a family of higher-order functions that analyze a recursive data structure and through use of a given combining operation, recombine the results of recursively processing its constituent parts, building up a return value. Typically, a fold is presented with a combining function, a top node of a data structure, and possibly some default values to be used under certain conditions. The fold then proceeds to combine elements of the data structure's hierarchy, using the function in a systematic way.

 

関数型プログラミングでは、fold(reduce, accumulate, aggregate, compress, injectとも呼ばれる)とは、再帰的データ構造を解析し、所定の結合操作によって、その構成要素を再帰的に処理した結果を再結合して戻り値を構築する高階関数群のことである。
通常、foldには、結合関数、データ構造のトップノード、および場合によっては特定の条件下で使用されるいくつかのデフォルト値が提示される。そして、foldは、その関数を体系的に用いて、データ構造の階層の要素を結合していく。

 

Fold/reduce「再帰的データ構造」を結合操作する高階関数、これは本書でもじっくりと書いたこと。

 

On lists[edit]

The folding of the list [1,2,3,4,5] with the addition operator would result in 15, the sum of the elements of the list [1,2,3,4,5]. To a rough approximation, one can think of this fold as replacing the commas in the list with the + operation, giving 1 + 2 + 3 + 4 + 5.

In the example above, + is an associative operation, so the final result will be the same regardless of parenthesization, although the specific way in which it is calculated will be different. In the general case of non-associative binary functions, the order in which the elements are combined may influence the final result's value. On lists, there are two obvious ways to carry this out: either by combining the first element with the result of recursively combining the rest (called a right fold), or by combining the result of recursively combining all elements but the last one, with the last element (called a left fold). This corresponds to a binary operator being either right-associative or left-associative, in Haskell's or Prolog's terminology. With a right fold, the sum would be parenthesized as 1 + (2 + (3 + (4 + 5))), whereas with a left fold it would be parenthesized as (((1 + 2) + 3) + 4) + 5.

In practice, it is convenient and natural to have an initial value which in the case of a right fold is used when one reaches the end of the list, and in the case of a left fold is what is initially combined with the first element of the list. In the example above, the value 0 (the additive identity) would be chosen as an initial value, giving 1 + (2 + (3 + (4 + (5 + 0)))) for the right fold, and ((((0 + 1) + 2) + 3) + 4) + 5 for the left fold. For multiplication, an initial choice of 0 wouldn't work: 0 * 1 * 2 * 3 * 4 * 5 = 0. The identity element for multiplication is 1. This would give us the outcome 1 * 1 * 2 * 3 * 4 * 5 = 120 = 5!.

 

まあこの辺のことは、Wikipediaのタイトル記事(別に数学的にWikipediaがおすすめ記事と言ってるわけではない、Wikipedia記事であってさえも、というニュアンスがある)でも、普通に記載されている。

 

ja.wikipedia.org

には、

再帰呼出しの例[編集]

ここでは、階乗計算を再帰呼び出しにより実装する例を紹介する(自身の再帰呼び出しが、その計算における最後のステップになっているような末尾再帰再帰呼び出しの特別なケースであることに注意)。

C言語での例:

/* 階乗 n! を計算する */
int fact(int n) {
  if (n == 0) return 1; /* 脱出条件。0! は 1 である */
  else return fact(n - 1) * n; /* n! は (n-1)! に n を乗じたもの。再帰呼出し */
}

という、コードが掲載されている。

実はこれは無名関数=ラムダ式であってもこのような再帰関数を定義することはできて、それがラムダ計算だけでも「繰り返し処理」ができるという重要な証明となっていて、チューリング完全だ、とか理論上は重要だ。

それは大変結構なことなのだが、理論上の重要性だけにとどめおかないで、これが階乗計算の関数型スタイルの作法だ、みたいに伝言ゲームをやらかしているアホな教科書や教師、そしてそれを鵜呑みにして疑わない、この記事で紹介した中級者みたいな2人あるいは3人のような人間がいる。

リアルのコードで階乗計算にトップダウン方向の再帰関数を書くようなプログラマーは「アホ」である。

それこそ末尾再帰のメモリ効率化の問題やら、なんども非効率に同じ計算が指数関数的増加するのでメモ化が必要になって、デメリットしかない。

課題そのものがディレクトリ構造のように再帰構造ならばまだ許容できるが、

本書で掲載したコードを引用すると、

 

const multiply = (a, b) => a * b;const product = natural(4).reduce(multiply);// 1 * 2 * 3 * 4console.log(product); // 24

とすることも自由に可能です。

 

と、普段普通に階乗の手計算でやるのと同じボトムアップでFoldするreduceを使えば良いことだ。そしてこれは本書の冒頭からずっと継承して説明しつづけているコードでもある。

本人の該当ツイートが消えているのが残念だが、それが上記のツイートであり、特に彼は「他にどういうやり方があるんですか?」みたいに聞いてきたので、その返答が、これである。

 

続きをかくと、

 

 

 

とりあえず一旦UPする。

 

どうせ本書に追記しようとはおもうので、

ImuutableJSを使ったコードはどっかに残ってるはずだし、コピペできるかもしれない。

 

 

【+α+タイプコンストラクタ編】本書いたら数学愛好者の中級レベルのプログラマーからマウンティングを試みられた話

本書いたら数学愛好者の中級レベルのプログラマーからマウンティングを試みられた話

ken-okabe.hatenablog.com

https://ken-okabe.hatenablog.com/entry/2021/12/12/102301

ken-okabe.hatenablog.comhttps://ken-okabe.hatenablog.com/entry/2021/12/12/130747 

ken-okabe.hatenablog.com

 のつづきです。

 

マウンティングを試みているが墓穴を掘り進んだ中級レベルの人が書いた素材はこれ

「関数型プログラミングが『銀の弾丸』であるという非常識な常識2022」の感想 - Qiita

qiita.com

 

まずはこれまでの総論というか、なんで中級レベルの彼がトンデモだと思っているのか、取り残した素材とあわせて論評。

 

本書で一貫しているのは、関数型プログラミングの力の源泉は数学そのものであり、プログラミングというのは、なるだけ簡素にその表記であれば事が足りる、ということです。

 

「なんで二項演算にこだわるのか?」というイチャモンに過ぎない雑音が多いですが、単に、モノイドでも

1+2+3

という二項演算子+があり、簡素に演算を表現できているが、少なくともJavaScriptでは他の本質的には二項演算として簡素に表現可能なものが、そのとおり表現できないので、この障壁を取り払おうとしているに過ぎません。

 

なんでJavaScriptなんだ?とか言ってる雑音集団はこういう中級者君のオレサマ今MLやってて気に言ってる最高、とか、その他何名か知ってるけど名前ひっぱってくるのは面倒くさいから容赦してやるけど、だいたい同類です。

 

基本的に、僕はJavaScriptTypeScript向けに関数型プログラミング入門を書きたいのだし、リーチできる初学者層が非常に多いのは書きごたえがあるわけです。

もうすぐRustにこの本を翻訳する予定ですが、とりあえずオレサマお気に入りのMLの啓蒙しろ、そのほうがよい

氏がそこまでJavaScriptにこだわる理由は私には分かりませんが、「アテにならない」TC39が支配するJavaScriptに無理やり二項演算子を定義するのではなくてもっと筋の良い言語の啓蒙に勤しむ方が活動として有意義だと私は考えます。

とか言ってくる分には、うっせーわ、人の本批判するにかこつけて、だまってろや?と口汚く罵るしかありません。一択でしょう。

MLなんぞの関数型本に巷の初学者には需要なんてほぼないでしょう。入門書書きたければてめーで頑張って書けばいいけど、ネガキャン乗じて「活動として有意義」みたいな趣味趣向のわけわからん政治的要求してくんなやド阿呆と口汚く罵る一択です。

 

まずこの辺から、この中級者に限らず文句垂れてる外野の属性っていうのはだいたい揃っていてヤバイ連中だな、と痛感しています。トンデモ臭もしますね。少なくともこういう連中は初学者の役になんて一切立ちません。関数型が難解であると思われてる元凶でもあります。

型は集合というnLabやらELMの作者やら権威を少なくとも彼らが黙り込むレベルの権威付をしてやると、行き場を失って右往左往しますが、今Twitterみたら、

 

 

もう必死に悪あがきしているようです。

仲間ももうnLabやElm作者があそこまではっきりと明言してるんだから、、と権威にはからっきし弱い、自分の頭で考えることが苦手な連中ですから、もうろくな助け舟も出ません。無理筋だとうすうす気づいてるんでしょう。

 

見るに見かねた人に

とおちょくられる始末。末期ですな。

 

なんで、同一視すれば楽に俯瞰できる、とこれだけはっきりと提示されているものに、

誰か「型って何?集合との違いは?調べてみました!」みたいな記事を書いてくれ

とか誰かに必死に頼まないといけないような差異を見つけ出すことに必死なんでしょうか?

この辺がこいつら中級者集団の浅はかなところです。

少なくとも彼らの知見から何かを学ぼうとしてる人は無駄だと思ったほうが良いでしょう。

僕は、同一視する、という概念を重視していて、それはより高い視点、メタな視点を獲得できるとおもっていて、初学者のひとらに必ず役立つ、楽に習得しやすいと、入門書を書く立場として大事なことだと思っています。

 

僕はそういう視点をもって、二項演算子の導入のメリットやら、型は集合である、と権威付もしながら紹介するわけですが、こいつら中級者はネガキャンはって妨害してきます。最終的には上のTweetでも明白でも自分のメンツだけです。

ネガキャンマウントとろうとしたがボコボコに返り討ちにされちゃって恥かいてメンツ保つことだけに必死です。

「型=集合」か?

「型」が何かというのは(私にとっては)難しい問題です。

初学者向けには「型は集合のようなものだよ」という説明で良いかもしれません。

初学者向けには、とか中級者ぶっていますが、実際こんなもん中級者君が何がわかってる、ってわけでもありません。

僕自身のことでいうと、いろいろ見ていて、ああ型は集合のことか、という直感というか同一性が自分でわかったので、そういう風に解説したいがどうせ中級者どもがわかった風に誹謗中傷してくるだろうな、と予測したので、軽くぐぐったら、nLabやらElm著者の記事やらがヒットしたのでそれをありがたく、そのとおりだよなー的に引用させてもらっているわけです。

Type theory versus set theory型理論 vs 集合論

Alternately, we could change our terminology so that what we have been calling “types” are instead called “sets”.あるいは、これまで「型」と呼んでいたものを「集合」と呼ぶように用語を変更することもできます。 Thus, words like “type” and “set” and “class” are really quite fungible. This sort of level-switch is especially important when we want to study the mathematics of type theory,このように、「型」や「集合」や「クラス」などの言葉は、実はかなり代替可能です。このようなレベルの切り替えは、型理論の数学を研究するときには特に重要です。

 

と直接的に言及されている、ごく一部分だけを引用翻訳していますが、リンク先の全文を見れば明らかですが、これは中級者君が主張するような「初学者向け」のコンテンツではありません。

全文その他にブログリンクもあり結構詳細に高いレベルで議論されています。

 

彼ら中級者は自分の頭で考えることが苦手だから今でもこういう状態だし、本は読むんでしょうが、実際出版されてる本でも、その著者の視点のスコープが限られていたらそこでオシマイです。そして実際こいつら中級者が、こんだけ大慌てしてるところをみると、本に書かれてなかったんでしょう。もちろん自分で気づくってこともないし、こうやって僕が情報をnLabの引用つきで書いても、読まずにどこですか?とかマヌケなことを言ってくるし、それを読んでもまだ納得もできない。

こういう連中は学問をするにあたって本当に適正がないです。頭がよろしくない。センスがない。

そして僕のアウトプットをトンデモレッテル貼りたがって、さらに評判を下げようと必死な連中っていうのは、だいたいこういう中級者の連中です。

とりまきは中級者以下なんで、そーだそーだ岡部のいうことは変だーと同調するだけで、なんかわけのわからん風評被害が醸成されていきます。ろくでもないですね、ほんとうに。

しかし、私が思うに、型は集合よりも幾分抽象化されています。ここでの抽象化というのは、できる操作が限られているという意味です。例えば、

あなたがおもう「幾分」ってどれくらい?って話になりますが、事例を出すそうなので、その「幾分」ぶりを精査してみよう。

  • 集合は自由に和集合 (union) や共通部分(交差, intersection)を取れますが、型について和や交差を取れるかどうかは型システムに依存します。
    • 例えば、今のTypeScriptにはunion typeやinteresction typeがありますが、リリース当初のTypeScriptにはどちらもありませんでした(union typeはTypeScript 1.4から、intersection typeは1.6からです)。
    • 別の例で言うと、Haskellにはそもそも部分型関係がないためunionもintersectionもありません。

なるほど。「型システムに依存する」と、その実例としてTypeScriptの実装の歴史を語っている。Haskellの実装の不備のことを不満に思っている。

XXかとww(自粛

なんで、数学構造、数学の理論が各プログラミング言語の型システムという実装に依存する、とか言ってるのでしょうか?

逆なら真であり、実装が数学理論に依存するのです。

数学理論が実装に依存する、なんて馬鹿げた話なんてありえません。

型システムが高度化していく歴史の下敷きには、その先に型理論があるわけで、それは究極的には集合論のことだ。

「抽象化」のはなしをしているのに、なんで実装の違いの話をしているんだろう。

「できる操作が限られている」のが実装の至らなさであるのならば、それは「抽象化」の問題とは言わない。抽象概念を実装するという「具体化」の問題なんだ。

>集合は自由に和集合 (union) や共通部分(交差, intersection)を取れますが、型について和や交差を取れるかどうかは型システムに依存します。

集合論という抽象的な概念、数学があり、それをどうやって具体的な実装をしてコードに表現できるかだろう?

これで、なんで集合は型ではない、という論証になると思っているのか。そうとう頭が(自粛)ですね。なんで僕の仕事を批判できるとか思ってるんだろうか?よくわかりません。

  • 集合に対しては冪集合 (power set) という構成がありますが、型に対する類似物はあまり聞きません。ある型の部分型全てを集めた型…となるのでしょうか?
    • 【追記】「A -> Bool がそうではないか」という意見を頂きました。特性関数と部分型を同一視すればそうなりそうですが、筆者としては「部分型を集めた」感がないなあ、と思ったのでこの記事の当初の版では言及しませんでした。

うん、結局、集合のほうがより明確に抽象概念化されているんだ。これは本書ですでにまとめていて、たとえば

f:id:stq2050:20211212212948p:plain

この表ひとつとっても、集合論のほうは細分化されているけど、型は型としてしかないのはわかります。他の分野では区別があるものでも型は区別されてない。

 

という点で型と集合は違うものだと考えます。

?どういう点で?

君ひとりが混乱してます、って言ってるだけ、あるいは、型のほうは集合論みたいに十分整理されていない、あるいは整理しにくい現状だ、あるいは、TypeScriptで十分実装されてない、って言ってるだけだろう?

それをもって「型と集合は違うものだ」という論証としてなんて成立していない。

論証成立もしてないのに、してる、とおもえるならばトンデモ重症レベルだと思う。

 

繰り返しますが、私は初学者向けに「型は集合のようなものだよ」と説明することを否定はしません。

はっきり言うけど、君は「初学者向けに」みたいに豪語できるような知見がある立場になんていないよ。

しかし、Twitterで「こいつ @mod_poppo は、型が集合である事実すらしあず」と仰っているからにはKen Okabe氏は本気で「型=集合」と考えているようです

そうだね、そう考えてるよ。何か問題でも?

FunctorとMonadは二項演算か?

これについては、もう十分論評したとおもう。

Haskellのコードでゴネてたけど、Haskellモナドで全部二項演算子だし。

ゴネた挙句に、関数がファーストクラスになる実装によってモナドの二項演算になっている、とか、また、実装を数学構造の定義や差異の要因にしようとしていた。

TypeScriptの実装のバージョン違いが、型と集合の数学構造の抽象度と関係あるとか、この形式のトンデモ理論を振り回すのが好きな中級者なんだろうと思う。

はなしにならん。

「タイプコンストラクタ」の用法

詳しい人向けの解説:氏の文書では「タイプコンストラクタ」を「(モナド等の)単位射」の意味で使っています。これは間違いです。

デマ。

詳しい人向けだろうがそうでなかろうが悪質なデマ。そんな事実はない。

 

本書の範囲には含めなかったが、タイプコンストラクタは

  • 0階のタイプコンストラク
  • 1階のタイプコンストラク
  • 2階のタイプコンストラク
  • 3階のタイプコンストラク
  • ・・・・・

というように、あらゆる階層に存在する。

こういう高階のタイプコンストラクタは、高カインド型とも言われている。

 

本書でもまる一章かけて解説した高階関数

f > g あるいはg(f) というように関数をパラメータとする関数である、という同じ意味での高階(HihgerOrder)。

関数は値のコンストラクタとすると

型コンストラクタはその型レベルのはなし。

  • 1階のタイプコンストラク

では、単に

F<A>

というようになり、本文を引用すると

今は、かんたんな配列なので、[]でくくるだけで暗黙のうちにそのArray型を生み出しているわけです。きちんと明示しておくと、単純に、

const F = // type constructor a => [a];

となります。こういう何もないところから、ある特別な型(Type)を新規に構築する(生み出す)関数のことを、型構築子/タイプコンストラクタ(Type constructorといいます。オブジェクト指向でいうところの、コンストラクタConstructor)に該当します。 TypeScriptにおいてはタイプコンストラクタにも当然きちんと型(Type)がつくのですが、ジェネリックを使って、以下のようにタイプ定義します。

type F<A> = A[];const F = // type constructor <A>(a: A): F<A> => [a];

 

 

となるから、それで良い。

これはモナドの単位射に他なりません。

まあそのとおりだね。モナドはそうなるように定義されてるんだから。

 

用語に関してついでに言うと、Ken Okabe氏の文書ではMonadを返す関数のことを「Monad関数」と呼んでいるようです。Haskellで言えば a -> m b みたいなやつです。圏論の文脈的には、これにはKleisli射という名前がついています。

そのとおりだね。

そして中級君は、あんまり自分の頭で考えるのが苦手だから、用語は本の丸写しみたいな思考しかしていないのがわかるけど、君はクライスリ圏の概念の意味とかちゃんとわかって発言している?自分の言葉でちゃんと説明できるのかい?

どうせできないんだろう?せいぜい本の引用だけだろう?

クライスリ圏っていうのは結構、特異な発明であって、基本は集合の圏だ。発明者たちの特殊な思考フレームワークを特別に信奉したいという事情でもない限り、関数型プログラミングは普通に集合・関数の圏(Category of sets)で関数合成からはじまって必要な部品は揃うんだから。

 

自分で意味も理解してないような用語を振り回して、

圏論の文脈的には、これにはKleisli射という名前がついています。

とか書くことに一体なんの意味があるんだい?

 

まともに論じる能力もないのに中級者として背伸びして難しそうな用語でハッタリ効かせようと思っているだけなんだろう?

Ken Okabe氏の22.9には

基本的に、プログラミングを含む工学では、なるだけ既存の数学的な概念と用語を踏襲すべきであって、同じ意味の造語を無闇に増やすことはあまり意味がないどころか、混乱をもたらすだけだと考えます。

という記述があります。私としてはこの記述に非常に同意します。Ken Okabe氏自身がこれを徹底していればもっと良かったと思います。

 

そうだね。でも中級者君は口では「非常に同意します」とかいうだけで、使えもしない、つかってもいないクライスリ圏の射という概念を、集合の圏で十分なところに積み増してるよね。

 

実行できてないじゃない。

 

僕は徹底しているからそんな用語は無用に使わないようになりました。初学者に教えることが目的で、君のように権利を借りて背伸びしてハッタリ効かせる、てのはむしろ目的の障害になるんですね。

 

だいたい、Haskellモナド関数でも、普段いちいち射(Morphism)だ、みたいに取り扱っているのかい?クライスリの射だ!とか普段やってる?やってないだろ?

それは「関数」と呼ぶんだよ。巷をみるとたとえばStackoverflowでもMonadFunctionでこの概念はやりとりされている。クライスリの射という用語自体のQAはあるんだろうが、モナドの関数の呼称としてやってるのなんて’見たこともない。自分でも普段そんなの使ってないくせに、なんで初学者に教えるようなフェイズで、突然えらそうに大上段から、

Haskellで言えば a -> m b みたいなやつです。圏論の文脈的には、これにはKleisli射という名前がついています。

みたいなことを言う必要がある?学習者ビビらせて偉そうにしようとしてる?

あと、僕に対してマウンティング取りたいだだけだろう?閲覧者にむけて良いカッコつけてメンツ保ちたいだけだろう?

全部キミの都合じゃないw

 

最後にうまいこと言ったと思ってるんだろうけど、自分でできてないことを賛同する、僕ができてることを、彼がもっと徹底できていればもっと良かった、とか、うまいこと言ったつもりなんだろうけど、正直、鼻で笑っているよ。

いかに中級者君のような態度が初学者の学習の妨げになっているのか自覚してほしいね。そんで僕の仕事に茶々入れてくんな、迷惑だし有害だし、君にそんな知見なんてないんだからさ。

以上かな!

 

 

 

 

 

 

 

 

 

 

【追記への反論編】本書いたら数学愛好者の中級レベルのプログラマーからマウンティングを試みられた話

関数型プログラミングが『銀の弾丸』であるという非常識な常識2022

 

を書きました。

本書いたら数学愛好者の中級レベルのプログラマーからマウンティングを試みられた話

の続編です。

 

ken-okabe.hatenablog.com

 

ken-okabe.hatenablog.com

 

 

Qiitaのトンデモな記事に反論していきます。

qiita.com

 

「【追記】」の部分はTwitterでの他の人の反応や氏のはてなブログによる反論を受けて記載したものです。

 

結局の所、自分で勝手に火がついて、よし岡部の本をネガキャンするぞネガキャンするぞ、とやっておいて、結構不条理でトンデモなデマを流されているわけです。

その上で、「自分には時間が無制限にあるわけではない」というような、身勝手な被害者ヅラをした挙句、ちょっとこちらがちゃんとした反論をしたら

氏のはてなブログによる反論を受けて記載したものです、とくる。

こちらは時間も手間もかけて書いた著作を愉快犯の誹謗中傷みたいにやられて、徹底的にやると思ってるんで、時間が、、、とか言うなら足りなくなるんじゃない?

すでに書いたように、僕は君の事は中級者というよりむしろ理不尽で意味不明な攻撃してくるトンデモだと思っているし、そこを放置したら、またややこしいことになるので、徹底的に詰めます。

 

【追記】もちろんC++やRustでも演算子オーバーロードはできますが、C++やRustを含む多くの言語では演算子としてあらかじめ決まった種類のものしか使えません。「パイプライン演算子がないから |> を演算子として使おう」ということはできないのです。一方で、上の段落で挙げたML系言語では(言語が許す種類の文字からなる)任意の記号列を演算子として定義することができます。元の文書の主題からすると任意の記号列を演算子として使える方が好ましいかと考えたのでStandard ML, OCaml, Haskellを挙げました。

 

「あらかじめ決まった種類のものしか使えません」

どーでもいいですね、そんなことは。

それに、|>というシンボル自体になにか数学的な意味が関与しているわけでもなんでもありません。で、またML最高の話ですか?

自分のより好みをもって、人の著作を批判とかしなさんなよ?って話です。

僕の本の内容の正しさとあなたの好みとか、「演算子のシンボルが限られてること」なんて関係ないでしょ。

 

【追記】このセクションは特に私の「感想」色が強く、人によって意見が異なることは自然なことだと思います。なので、あまりとやかく言い争う気はありません。

「感想」というか、あたかもこちらの技術的アプローチに問題がある、というようなニュアンスで中級者君は好評したんだね。誰でもそう読み取れる。

JavaScriptにおいて二項演算子を導入するメリットは大きく、それは本書の表記に大きく関与していて、文字数が多くなっただの行儀が悪いだの、ご丁寧にサンプルコードまでつけて、ネガキャンやった。

それで「感想」で人によって意見が異なる、とか、しれっと言う。まあ論評は自由だけど、理不尽なネガキャンはバズるので、こちらとしては看過しないし、言い争いは当然する。

 

【追記】initialValueを省略した場合に「加法の場合は0となる」「乗法の場合は1となる」という説明は間違っています。だって空配列に .reduce(add) を適用しても 0 は返ってこないでしょう?氏の反論記事では空配列のことを無視しており、私の文章の書き方が悪かったかなあと反省しております。

間違ってはいない。サンプルコードにおいては実際にそういう挙動になる、と整合的に理解できて、それは二項演算のFoldの理解に大変重要です。

 

空配列、空の値、Noneその他は、まるで別の問題で、各言語のFold/reduceの実装と一緒くたに論じるような話ではありません。

空の値のはなしはTypeの話であって、TypeScriptで空配列をどう扱うかにも関係してきます。

僕が中級者君がトンデモだと思うのは、数学の代数構造の話と、言語に実装されたreduceの仕様の挙動を一緒くたにして平気だということです。文章の書き方以前の問題だと思う。

 

【追記】私は「型≠集合」という立場なので関数型プログラミングモナドが集合圏上のモナドと同じものだとは思いません。

 

あなたは勝手にすれば良いが、こちらの立場を否定するな。

それはnLabやELM作者の主張と同一であり、それは引用して明示している。

最初読んでもなかったよね?

Type theory versus set theory型理論 vs 集合論

Alternately, we could change our terminology so that what we have been calling “types” are instead called “sets”.あるいは、これまで「型」と呼んでいたものを「集合」と呼ぶように用語を変更することもできます。 Thus, words like “type” and “set” and “class” are really quite fungible. This sort of level-switch is especially important when we want to study the mathematics of type theory,このように、「型」や「集合」や「クラス」などの言葉は、実はかなり代替可能です。このようなレベルの切り替えは、型理論の数学を研究するときには特に重要です。

以上は、nLab記事からの引用文です。

nLab は、数学・物理学・哲学の研究レベルの内容について扱ったウィキである。nLabはMathOverflowにおいて、質問前にチェックするべき標準的なオンラインの数学文献の1つとしてリストされている。多くの質問と解答が、nLabを背景資料として用いている。また、nLabはバイエズがアメリカ数学会American Mathematical Society)に投稿した数学ブログのレビュー記事の中で言及されている2つのウィキのうちの1つである。

Types as Sets集合としての型

One of the most important techniques in Elm programming is to make the possible values in code exactly match the valid values in real life. This leaves no room for invalid data, and this is why I always encourage folks to focus on custom types and data structures.In pursuit of this goal, I have found it helpful to understand the relationship between types and sets. It sounds like a stretch, but it really helps develop your mindset! Elmプログラミングで最も重要なテクニックの一つは、コード内の値を実際に有効な値と正確に一致させることです。これでは無効なデータを残す余地がないので、私は常にカスタム型やデータ構造に注目することを推奨しています。この目的を追求するにあたって、型(types)と集合(sets)の関係を理解しておくことが役立つことを私は発見しました。余計なことのように聞こえますが、それは貴方のマインドセットの形成に本当に役立つのです!

 

この役に立つ視点は、初学者にとっては非常に有用。

そしてnLab原文リンクを開いてみればわかるとおり、これはそもそも初学者相手に解説されているわけでもありません。

そして中級者君の勝手な趣味的な自己満足立場をもしこちらの立場が間違っていると印象操作する形で押し付けるならば、それは学習者全てにとって有害だ。

自分の考えを表明するのは自由だが、こちらがnLabその他の文献をひっぱって明確にしてるもんを否定する形で発言しないでほしい、迷惑なんで。

こうやって反論することでようやく彼らがフェアな判断ができるようになる。

なんで、nLabの元文献とか引用しない?

Qiita記事の閲覧者に読まれたら自分の主張が揺るぐ、賢明な読者ならばどっちの立場が正しく有用なのか、判断されて自分のメンツがつぶれることを恐れているからだろう?

君のメンツでこちらの著述が否定されることが本当に腹立たしい。

行儀の悪いやりかたしか君はできないんだね。

 

【追記】私は「型≠集合」という立場なので関数型プログラミングモナドが集合圏上のモナドと同じものだとは思いません。

 

それに、二項演算のはなしだよね。

wiki.haskell.org

 

f:id:stq2050:20211212130338p:plain

 

以上のようにHaskellにおいてモナドというのは、

>>= という二項演算子をもって、

二項演算の等式としてモナド則(Monadlaws)でもなんでも書かれているんだけど、

中級者君のトンデモ主張っていうのは、この眼の前の現実と合致しない。

中級者君の勝手な立場は黙ってくれたらそれで有害ではなくなるけども、

関数型プログラミングであつかう集合の圏のモナドは、

このように間違いなく二項演算子、として掲示されている現実がある。

 

現実のコードと、君の勝手な主張が不一致ならば、

間違っているのは中級者君であるのは間違いない。

いくらゴネようとも、もう無理筋だって閲覧者にバレていることだろう。

 

今回は以上です。

 

ken-okabe.hatenablog.com