住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?
住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか? の続き
『関数型プログラミングに目覚めた!』のレビュー(Day-1)の嘘と誤魔化し ー 自分たちが非実用的な「机上の空論」を語っている事をけして読者に語らないこと
TimeEngine お絵かきアプリ(関数型リアクティブプログラミング/FRPのトイプログラム)
「これ以上は説明も回答もしない。」という嘘説明のFRPの基本原理コードなるもの
OCamlでの関数型コードのGUIアプリ課題 あらあためて、住井 英二郎@esumii (東北大)、らくだの卯之助@camloeba、@nonstarterその他へむけて
C言語は純粋関数型である。 「純粋関数型」と「参照透明性」国内の自称関数型コミュニティの胡散臭さと海外論調の違い
TimeEngineの __x.t = __x.t + 1; について
この辺の続きとなります。
特に前回、
スケールするFRPアプリ課題:わかりやすい嘘誤魔化しをしないように
この課題についてです。
FRP必要ない、とかぶちあげてた@nonstarterを「擁護」あるいは結託してたんだから、FRP使うとかもちろんなしで。
純粋関数型の「状態渡し」で実用的なアプリを「なんら困難もなく」書けるんでしょ?
FRPと複雑性は変わらんのでしたよね?じゃあ、当方のFRPのコードと同等以下の複雑性で、
関数型「状態渡し」をもって書けるはずです。
もっとも適当な嘘ハッタリじゃあなければね。さあやって見せてください。どうぞ。
と煽ったら、まんまと書いてくれました。ご苦労様。
これまで連中がやらかしてた嘘誤魔化しを徹底的にこちらが糾弾する形で、追い込んだので、
(そういう作業も手間がかかるんですよね、最初から誤魔化ししなければいい)
ここに来てようやくそれなりに嘘ごまかしが排除された実用的GUIアプリの比較対象となりうるコードが登場してきました。
まあ、さぞかし「大変」だったことでしょう(笑)
連中が総体として、傍観者、客観的読者へ向けて印象づける
「いともかんたんに、なんでも関数型の状態渡しでできる、OCamlでも何でも一緒」
「FRPも状態渡しも一緒、FRPはなんら必要なし、メリットなし@nonstarter」
という趣旨の大嘘@はとりあえず確定したようなので、確認していきましょう。
OCamlソースコード
module H = Dom_html
let g x = Js.Opt.get x (fun () -> assert false)
let o x = Js.Opt.return x
let s = Js.string
let i = string_of_int
type ('a, 'b) state =
(* just to omit explicit annotation of complex types by (ab)using "polymorphism" *)
{ itemss : 'a list list; cur : int; selects : 'b }
let _ = H.window##onload <- H.handler (fun _ ->
Firebug.console##debug(s "DoNe2 starting");
let d = H.document in
let tm = g (d##getElementById (s "todo2_time")) in
ignore
(H.window##setInterval
(Js.wrap_callback (fun _ ->
tm##textContent <- o (jsnew Js.date_now ()##toString ())),
100.));
let nu = g (H.CoerceTo.input (g (d##getElementById (s "todo2_new")))) in
let selects = g (d##getElementById (s "todo2_selects")) in
let select1 = g (H.CoerceTo.input (g (d##getElementById (s "todo2_select1")))) in
let selected = g (d##getElementById (s "todo2_selected")) in
let input = g (H.CoerceTo.input (g (d##getElementById (s "todo2_input")))) in
let go = g (H.CoerceTo.input (g (d##getElementById (s "todo2_go")))) in
let ul = g (H.CoerceTo.ul (g (d##getElementById (s "todo2_ul")))) in
(* ここまでDOMの準備。ここから本体 *)
let rec generate_handlers st =
let rec handle_sub _ =
let v = input##value in
let rec add_v pos = function (* 補助関数 *)
| [] -> assert false
| items :: itemss when pos = 1 -> (items @ [v]) :: itemss
| items :: itemss -> items :: add_v (pos - 1) itemss in
let new_itemss = add_v st.cur st.itemss in
Firebug.console##debug(s ("DoNe2 adding to List#" ^ i st.cur ^ ": " ^ Js.to_string v));
input##value <- s "";
configure_ui { st with itemss = new_itemss };
Js._false
and handle_nu _ =
let new_list = H.createInput ~_type:(s "submit") d in
let new_selects = st.selects @ [new_list] in
let new_itemss = st.itemss @ [[]] in
let new_cur = List.length new_selects in
new_list##value <- s ("List#" ^ i new_cur);
Dom.appendChild selects new_list;
configure_ui { (* st with *) itemss = new_itemss; cur = new_cur; selects = new_selects };
Js._false
and handle_select new_cur _ =
configure_ui { st with cur = new_cur };
Js._false
in
(handle_sub, handle_nu, handle_select)
and configure_ui st =
selected##textContent <- o (s ("List#" ^ i st.cur));
go##value <- s ("NewDoNe#" ^ i (1 + List.length (List.nth st.itemss (st.cur - 1))));
let rec rm_all_child p = (* 補助関数 *)
Js.Opt.case (p##firstChild) (fun () -> ())
(fun c ->
ignore (p##removeChild(c));
rm_all_child p) in
rm_all_child ul;
List.iter
(fun item ->
let li = H.createLi d in
li##textContent <- o item;
Dom.appendChild ul li)
(List.nth st.itemss (st.cur - 1));
let (handle_sub, handle_nu, handle_select) = generate_handlers st in
go##onclick <- H.handler handle_sub;
nu##onclick <- H.handler handle_nu;
List.iteri
(fun n selected ->
selected##onclick <- H.handler (handle_select (1 + n)))
st.selects
in
configure_ui { itemss = [[]]; cur = 1; selects = [select1] };
Js._false)
まず、「非常に長い」です。
これまで、あたかも簡単に短く書けるようなフリしたコードを出していたけど、ほんの少し複雑にスケールしたアプリが課題になると、途端に急にコードが倍増した。なんで?
これまで課題が簡単である、というある種の特性を利用し、それが複雑になるとすぐに破綻するコードである、という、学習者にとってもっとも重要な情報をひた隠しにし、あたかも汎用的に通用するスケールする設計とコードである、というフリをしてきたのが、実際に破綻してしまったからです。
つまり、これまで誤魔化していた、課題の単純性という特性の利用、誤魔化しが通用しなくなり、破綻して、一気に膿が出てきた分量が、この目に見えるコード量の急増分です。
言い換えると、すでにこうやって急増したコードをベースに、いろいろ削る形で修正すれば、一個前のより単純な、ToDoリストアプリが簡単に書けます。そして、より単純な、ToDoリストアプリからまた順当に機能追加すれば、このコードになるでしょう。こういうのがスケールする設計で、コードであると言います。
前回からまるで様子の異なった、コードがなんか倍増しているということは、前回の設計が、問題がスケールしたら、まったく通用せず破綻した、ということを意味します。
この人物のコメントです。
動いてると思うけど書き殴りだから細かいことは気にしないでね
単に機能が増えて面倒になっただけで、状態渡しのしかたは前回と同じ。ちなみに今回も頑張れば状態渡しすら使わずに書けそうだけど、これは使ったほうが楽かな。
メインループではなくハンドラでOCaml部分の状態渡しをしてるので、「メインループを書き直してるからスケールしない」というのとは少し違う。
(状態渡しや関数型云々とは無関係に)やっぱりDOMかつ静的型だとちょっと長くなるね(定数倍ファクターなのでスケール云々とも無関係)。前回のから改造しててOCaml部分の不整合はデバッグしなくてもコンパイラが見つけてくれるので開発は明らかに楽だったけど。
まあ
それなりの試行錯誤の末に「苦労したんだな」
というのが行間から溢れ出ています。お疲れ様。
実際に、前回この人物は、「状態渡し」で書きたくない理由についての言い訳として、
以前の「状態渡し」のほうがOCaml上のGUIアプリの実装としては異常。書けないとか言うから反例として書いただけ。OCamlは非純粋関数型なので、普通は副作用(破壊的代入を含む)を使う。FRPを含め、純粋関数型のGUIプログラミングがしたかったらHaskellのライブラリなりElmなり、それ用の適切な道具を使えば良い。
と発言しており、
「状態渡し」のほうがOCaml上のGUIアプリの実装としては異常
つまり、今回慣れない、かなりイレギュラーな実装を試みた、というのがわかります。
「非常に長いコードである」という点については、
静的型だとちょっと長くなる
らしいです。つまり、
実用的なGUIアプリをOCamlで静的(イミュータブル)な関数型のスタイルをもって実装しようとすると、簡潔になるどころか、コードがかなり煩雑になり長くなる
ということが、反発していた、誤魔化し続けていた当のOCamlプログラマー自身の手により実証されました。
すなわち、これが、この人物を含め、胡散臭い連中がずっと誤魔化してきてけして表明しなかった動かしがたい事実であり、私が、当初、あんたら関数型について偉そうな事言ってるけど、スケールする実用的なGUIアプリとか、そのやリ方ですんなり書けるわけ?と批判した核心的な要素となります。
@nonstarterはじめ、連中がさんざん中傷してきた、JavaScriptの関数型において、
いまやGUIフロントエンドのデファクトとなってしまっている、FacebookReact、
それに、私のTimeEngineというFRPライブラリを適用してFRP、関数型で書いた、同じGUIアプリのソースコードは以下でした。比較検討のために再掲します。
スケールするFRPアプリ課題:わかりやすい嘘誤魔化しをしないように
TimeEngine公式サイト
http://timeengine.github.io/
のDemo11
ソースコード(index.jsx)
const TodoElement = () => {
const ClockElement = __Element(__.intervalSeq(Immutable.Range(), 100)
.__(() => (<div>{moment().format('MMMM Do YYYY, h:mm:ss a')}</div>)));
const __fdList = __().__((val) => (__.log.t = val));
const __lists = __(true).__((__list) => (__fdList.t = __lists.length));
const ListofListElement = __Element(__([__lists])
.__(() => ((__lists).map((list, i) => (
<button onClick = {() => (__.log.t = __fdList.t = i)}>{'List#' + (i + 1)}</button>)))));
const InputElement = () => {
const __value = __();
const onChange = (e) => (__value.t = e.target.value);
const onClick = () => (__.log.t = __lists[__fdList.t].t = __value.t);
const onClickNL = () => (__.log.t = __lists.t = __(true));
const __seqEl = __([__value]).__((value) => (<div>
<h4>{'List#' + (__fdList.t + 1)}
<button onClick = {onClickNL}>{'NewList'}</button></h4>
{__lists[__fdList.t].map((item) => (<li>{item}</li>))}
<input type="text" value={value} onChange={onChange}/>
<button onClick = {onClick}>{'NewToDo#' + (__lists[__fdList.t].length + 1)}</button></div>));
__.log.__(() => (__value.t = ""));
return __Element(__seqEl);
};
__lists.t = __(true); //new items-list
const __delay = __.intervalSeq(Immutable.Seq.of("started!"), 1000)
.__(() => (__.log.t = "showInput"));
return (<div><h2>ToDo</h2>{ClockElement}<p/>
{ListofListElement}<p/>{InputElement()}</div>);
};
const mount = ReactDOM.render(TodoElement(), document.getElementById('todo'));
公平性のため、空行、コメントを除外して、コードの行数の単純比較
をしてみると、
OCamlの関数型状態渡し : 74行
JavaScript+React+TimeEngineのFRP : 29行
です。もちろん、あちらも当方も、いくらでも改行は整理することは可能ですが、
可読性とバランスを双方とっているようにみえる現行のコードでの単純比較となります。
同じGUIアプリを関数型で実装するのに、JS+TimeEngine(React)のFRPでは、OCamlの状態渡し関数型より、行数にして半分以下
で書けている、ということが証明されました。
まあ、視認しても、ぱっと見でも、どちらが簡潔に短く表現できているのか?
一目瞭然だと思います。
そして念の為ですが、ちょっと例題を複雑にしたらこうなった、ということであり、
より複雑性が増したら、さらにこの差は顕在化し、デバッグその他の差異は深刻になる、
というのは明らかです。
たとえば、私が書いた上記のFRPのコードですが、
const
で定義している定数をそのまま抜き出して列挙してみると、
これらはそのまま関数型コードの構造になっています。
const TodoElement
const ClockElement
const __fdList
const __lists
const ListofListElement
const InputElement
const __value
const onChange
const onClick
const onClickNL
const __seqEl
const mount
こうなります。GUI部品のReactのElementは、
ToDoElementが、最後にmountされる一番外側のコンポーネントであり、
それらの内訳は以下の3つ。
- ClockElement
- LIstOfListElement
- InputElement
さらに、3つのユーザ入力イベント宣言
- onChange
- onClick
- onClickNL
に加え、FRP値
- __fdList (現在フォーカスされているリスト)
- __lists (リスト群)
- __value (ユーザ入力テクスト)
- (__seqEl)
以上の各const
の宣言を列挙することは、そのままアプリの設計の概略となっています。
関数型、宣言型コード全体として極めて見通しのよいコードになっています。
当然、アプリケーションがいくら複雑になろうとも、このシンプルな構造の延長でいくらでもスケール可能です。
さて、このような明瞭簡潔な構造が、この人物が苦労して書いた「スパゲッティ・コード」
OCamlの状態渡し関数型コードにあるでしょうか?
動いてると思うけど書き殴りだから細かいことは気にしないでね
単に機能が増えて面倒になっただけで、状態渡しのしかたは前回と同じ。ちなみに今回も頑張れば状態渡しすら使わずに書けそうだけど、これは使ったほうが楽かな。
やっぱりDOMかつ静的型だとちょっと長くなるね
前回のから改造しててOCaml部分の不整合はデバッグしなくてもコンパイラが見つけてくれる
こういう発言からも明らかでしょう。
つまりこのコードは、コンパイラが不整合を見つけてくれないと、コーダーには手に負えないほど複雑であり、「面倒」であり、「動いてると思うけど、細かいことは気にしないでね」と言い訳せざるをえないほどに、設計についてはあやふやで、関数型でイミュータブルに書くには「異常」な努力が必要で、当然、長いコードである、
ということになります。
不整合をコンパイラが見つけてくれる?
私がJavaScriptのFRPのコード書くときは、そんなもんは必要ないですが?
この人物の最後の言い訳です。
俺もOCamlの勉強になったけど、「できない」と断言されたことをすぐにやって見せたんだから(それも3回。しかも他人に「課題」とか言った本人は1年がかり、かつ関数型ではなく全くの命令型。決して認めないようだが。)、もうつきあう義理はないよね。
まあ、こうやってちょっと本気出されて、徹底的に嘘ごまかしが通用しないレベルまで引き上げられて、こっちがあっさり書けるコードを、コンパイラに不整合チェックしてもらわないと自分のコードの妥当性すら確信できないような複雑なコードを書かざるを得ない、関数型で破壊的代入のないイミュータブルなコードを書くってだけで、むしろOCamlによるGUIでは通常らしい破壊的代入、命令型より「長くなる」、ということを痛感して、自分らが主張していたことが「机上の空論」であった、とようやく理解できたんじゃないかな?
「できない」と断言されたことをすぐにやって見せた(それも3回)
正確に書き直してあげると、こっちの議論、批判の趣旨くらいは理解していたはずだ。
それを踏まえて、例題が、カウンターやお絵かきと単純すぎたことを良いことに、
「あたかもスッキリ簡単に書ける」ように嘘誤魔化しをし、
上記馬脚が顕れたような要素、つまりこちらが当初から指摘し批判していたことについてまるで認めなかった誤魔化しが2回あり、
最後の1回(今回)で、それが、
「やっぱりできなかった」ということをパフォーマンスして見せた、つまり、
FRPと複雑性は変わらんのでしたよね?じゃあ、当方のFRPのコードと同等以下の複雑性で、 関数型「状態渡し」をもって書けるはずです。
という課題について、
コンパイラの整合性チェック頼みの、君自身が実装に自信がないような言い訳するしか無い「 補助関数 」なるものがある複雑怪奇かつ倍の行数以上の長く煩雑なコードしか提示できなかった、
のだから、
「絶対出来ない」という当方の予想通りであり、課題は不合格
ということになります。残念でした・・・
これまで君が書いたコードはすべて、その延長線上ですぐに破綻する、スケールしない設計であった、ということが今回証明されたのだから、
「できもしないことを簡単にできるように見せかけてきた」ことが白日の下にさらされた、
にすぎない。
かつ関数型ではなく全くの命令型。決して認めないようだが。)
何度も繰り返すけど、TimeEngineの __x.t = __x.t + 1; についてで、説明してあげたことについて、
まだ駄々をこねたいのならば、あいもかわらず馬鹿だね、とあきれるしかないし、
何を中傷しようとも、
const TodoElement
const ClockElement
const __fdList
const __lists
const ListofListElement
const InputElement
const __value
const onChange
const onClick
const onClickNL
const __seqEl
const mount
というように、こちらの書く、FRPのコードは、すべてconstantに静的にコードがすっきりと設計されており、
一方で、そちらのコードは、そんなすっきりとした設計などどこにも存在しておらず、
まさに誰かさんがみたら「スパゲッティ・コード」と評するしかない代物であり、
現実に、
動いてると思うけど書き殴りだから細かいことは気にしないでね
などと書いた本人が、動作の妥当性についてすら、自信をもって提示できないという言い訳をいきなりかましたり、
前回のから改造しててOCaml部分の不整合はデバッグしなくてもコンパイラが見つけてくれる
などと、不整合について、設計段階では確信すらもてずコンパイラ頼み、
という惨憺たる状況を自分自身で白状してしまっている。
イミュータブルなコードを書く関数型プログラミングのGUIのアプリの適用について、
この惨状が一般的な真実であることは、
以前の「状態渡し」のほうがOCaml上のGUIアプリの実装としては異常。書けないとか言うから反例として書いただけ。OCamlは非純粋関数型なので、普通は副作用(破壊的代入を含む)を使う。FRPを含め、純粋関数型のGUIプログラミングがしたかったらHaskellのライブラリなりElmなり、それ用の適切な道具を使えば良い。
と、今回のような、極めて初歩的なGUIアプリの実装の局面でさえ、
今回自分で書いた関数型のアプローチが、
異常、不適切
であることをすでに認めてしまっていることからも、語るに落ちている状態。
以上のような、自信の悲惨なパフォーマンス、こちらがきちんと詰めたら、しっかりと嘘誤魔化しが暴かれた、証明された、という自覚くらいはもってるだろうから、
もうつきあう義理はないよね。
と逃げたくなる気持ちはよくわかる。
そうだね、今回もうはっきりとした結果は出た。万人が追認してくれるだろう、非常にわかりやすい結果だ。
これ以上やっても、結果は同じなので、これ以上、こちらがハードルを上げる意味もないだろう。
いや、あるかな?こういうGUIアプリなんて、こっちは、いくらでも複雑に書けるんだよ、何の苦労もなくね。
でも、これ以上複雑にしたら、もうそちらは、関数型で書くのは非常に苦しくなるんだろう?
そういうのを確認してやるのも面白いと思うけど、あらかじめ逃げの先手うってるところをみると、
もう限界っぽいし、繰り返しになるが、必死にコード書けたとしても、結果は同じだよね?
「中国製不正アクセス機器を使って誹謗中傷してる犯罪者集団」みたいな妄想はマジ勘弁。それを釣りで煽ってる奴ら(単独?)もな。
うん、最後になるが、これ書いてるの多分、
http://okaml.blogspot.jp/2016/05/done.html
で、
たとえ「過去の値」が不要になっても
と、追記して強調する癖とか、例のQiita記事の@lambada と全く同一の挙動であり、いわゆる駱駝、
で書いたように、こいつは、
らくだの卯之助@camloeba だと自分は思ってる。
らくだの卯之助@camloebaが、私を中傷する2chスレッドで、犯罪者集団と合流して、
Newbie伊藤 ◆p02ZTYXhPKG5
というコテハンで、
497:Newbie伊藤 ◆p02ZTYXhPKG5 :2015/12/26(土) 12:23:39.50 ID:vcolQJWW.net
956 仕様書無しさん 2015/12/26(土) 12:05:32.47
田中がaskの仕様変更からものの数日で実用的なGUIアプリをHaskellで作成して公開していて草
http://tanakh.jp/posts/2015-12-20-tomori-nao.html
499:Newbie伊藤 ◆p02ZTYXhPKG5 :2015/12/26(土) 12:26:12.92 ID:vcolQJWW.net
くらえ、素人!
と、下劣なAAを貼り付けて喜んでいるのは、上記リンクで説明したとおりだし、
【弱者を放置して】赤木智弘30【アイマスラブ】
http://yomogi.2ch.net/test/read.cgi/sociology/1445809895/19
という、仲間割れメンバーのスレで、
19 名無しさん@社会人 2015/11/03(火) 21:00:52.89
369 :M7.74(catv?):2015/11/03(火) 12:06:52.44 ID:gYJokkH60
投稿者: 俺 (ID:0001) 投稿日時:15年 10月 11日 14:09
なので、当面は以下の通りで行きます。
いかんせん、Qiita以降の奴しか知らないので、不慣れなところはレギュラーが極力、バックアップ願います。
8賢枠 (暫定) 2015/10/02
・スキルハンターの鷹
・知の答弁人田中
・思考のゼロ除算ボレロ
・辣腕ギークのキャメル
・知のオーバーフロー加藤
・AIG(A-Jiro is Guru)
・菅本・ザ・ストリートプログラマー
・(空席)
以上。
暴露されたとおり、
・辣腕ギークのキャメル
という、バレバレの「ミッションネーム」を、@tikuwa_zeroという主犯によって与えられて、さすがにそれは、ということで、
Newbie伊藤 ◆p02ZTYXhPKG5にしたことも知ってる。
また
AIG(A-Jiro is Guru)
というのは、2chのそのスレで、「さすがAIGさん」とか「先生」とか言われてる人物なんだけれども、
これはもちろん、
東北大学の住井 英二郎@esumiiのことだろう。
今、舛添都知事の税金の問題が糾弾されているけれども、
君は、国民の税金をもらって仕事させてもらっている立場だよね?住井さん。
明らかに平日の勤務時間中に、2ちゃんねるで当方を誹謗中傷するスレッドにお友達の駱駝と一緒に書き込んで、「先生」だの、「8賢者」「ウィザード組」だの持ち上げられて、犯罪集団と合流して何やってるんだい?
またもちろん、今回のOCamlを書いた人間は、OCamlを勉強している人、などではなく、
ご指名ではないけど久々に書いてみました。
というのは、こちらが駱駝サンと「指名」しているから、本人が呼応した、
という当然の憶測を警戒するから、わざわざ書いているのであり、
俺もOCamlの勉強になったけど
などとその他、複数回、OCamlを勉強中の人間をアピールしているのは、「カモフラージュ」であると見ている。別に褒め称えるつもりじゃないけど、まあOCamlで飯を食ってる人間だけのことはあるね、君は断じて「OCamlを勉強している人」などではない。これは嘘だ。
短時間で「書き殴り」で書ける、それなり以上に手慣れた人間が書く実装とコード。
もちろん、FRPとか使ってないので状態渡しでかなり無理して書いているけど、無理したら書ける程度のスキルがちゃんとある。
じゃあなんで、
「OCamlを勉強している人」
という嘘ついて、カモフラージュしてるのか?プロだと特定されたら都合が悪いからだよね?
ご指名ではないけど久々に書いてみました。
とわざわざ自分ではない、とアピールしないと都合が悪い。
まああいかわらずだね。駱駝。名指しされて、黙っていられなくなって、
でも、オモテで自分だとやれば、リスクがあるもんだから、匿名ブログこさえて、
他の人間が書くの待つのもどかしいし、OCamlのプロの自分がやったほうが、
手っ取り早く思い通りのコード書けるってわかってるから、自分でやったんだろ?
いったい誰がその君のポッと出の、こさえた本人しか知らない匿名ブログをUPされるとほぼ同時に、
前々から、こっちを誹謗中傷する犯罪者集団の2chのスレに逐一貼り付けてるんだ?
おまえだろ?
とりあえず、
「OCamlの関数型できないことを、さもすんなりできるように喧伝していたいた」
君らの嘘誤魔化しが、
OCamlのイミュータブルな関数型プログラミングでGUIアプリ書くことなんて「机上の空論」である、
ということが、
自分自身の発言と、自分が書いたコードによって、
証明されてよかったじゃない。