【『関数型プログラミングに目覚めた!』のレビュー(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はいらんみたいなことをそちらが主張したんだ)でどうなるか、やってみて?

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