動いてると思うけど書き殴りだから細かいことは気にしないでねちょっと整理してコメント追加。こういうリファクタリングも静的型の言語は本当に楽。- 単に機能が増えて面倒になっただけで、状態渡しのしかたは前回と同じ。ちなみに今回も頑張れば状態渡しすら使わずに書けそうだけど、これは使ったほうが楽かな。
- メインループではなくハンドラでOCaml部分の状態渡しをしてるので、「メインループを書き直してるからスケールしない」というのとは少し違う。
- (状態渡しや関数型云々とは無関係に)やっぱりJavaScript + Facebook React(functionalでないreactive programming)に比べれば、DOMかつ静的型だとちょっと長くなるね(定数倍ファクターなのでスケール云々とも無関係)。前回のから改造しててOCaml部分の不整合はデバッグしなくてもコンパイラが見つけてくれるので開発は明らかに楽だったけど。
- 補足:静的型つけ言語に慣れてる人はよくやるのでわかると思いますが、一部だけ適当に改造・コンパイルしてみて型エラーが出たところから直す、を繰り返すだけでいつのまにか全体が完成して楽だった、という意味です。JavaScriptで同じことをやったらデバッグ地獄になります。ちなみに静的型っていうのは「イミュータブル」のことじゃないです……。あと、JavaScriptで変数にconstをつけても、参照されるオブジェクト(のフィールド)はconstにもfunctionalにもなりませんよ。
- 慣れてきたので削除も実装してみた。もちリストの破壊的更新もメモリリークもしない。これは関数型プログラミングの基本的リスト操作と、数行のUI部分だけで本当に楽だった(5分ぐらい?)。
- 俺もOCamlの勉強になったけど、「できない」と断言されたことをすぐにやって見せたんだから(それも3回。しかも他人に「課題」とか言った本人は1年がかり、かつ関数型ではなく全くの命令型。決して認めないようだが。)、もうつきあう義理はないよね。
- 「中国製不正アクセス機器を使って誹謗中傷してる犯罪者集団」みたいな妄想はマジ勘弁。それを釣りで煽ってる奴ら(単独?)もな。
K
2016年5月24日火曜日
DoNe2
2016年5月21日土曜日
Done
ご指名ではないけど久々に書いてみました。OCamlもJSも詳しくないので細かいことは知らない。(追記:入力消すの忘れてたので修正。)(追記2:「状態渡し」すら不要だからしなかったんですけど、どうしても状態渡しを使わないと納得しない人がいるようなので使いました。)
DoNe
module H = Dom_html let g x = Js.Opt.get x (fun () -> assert false) let o = Js.Opt.return let s = Js.string let _ = H.window##onload <- H.handler (fun _ -> Firebug.console##debug (s "DoNe starting"); let d = H.document in let tm = g (d##getElementById (s "todo_time")) in ignore (H.window##setInterval (Js.wrap_callback (fun _ -> tm##textContent <- o (jsnew Js.date_now ()##toString ())), 100.)); let txt = g (H.CoerceTo.input (g (d##getElementById (s "todo_text")))) in let btn = g (H.CoerceTo.input (g (d##getElementById (s "todo_button")))) in let ul = g (d##getElementById (s "todo_ul")) in btn##onclick <- H.handler (fun _ -> let v = txt##value in Firebug.console##debug (s ("DoNe adding: " ^ Js.to_string v)); let li = H.createLi d in li##textContent <- o v; Dom.appendChild ul li; btn##value <- s ("NewDoNe#" ^ string_of_int (1 + ul##childNodes##length)); txt##value <- s ""; Js._false); Js._false)
- 今回は「状態渡し」すら要らないので「お絵かき」より簡単。何をさせたかったのかまったくわからない。どうしても状態渡しを使え、というならば以下のとおり。
module H = Dom_html let g x = Js.Opt.get x (fun () -> assert false) let o = Js.Opt.return let s = Js.string let _ = H.window##onload <- H.handler (fun _ -> Firebug.console##debug (s "DoNe starting"); let d = H.document in let tm = g (d##getElementById (s "todo_time")) in ignore (H.window##setInterval (Js.wrap_callback (fun _ -> tm##textContent <- o (jsnew Js.date_now ()##toString ())), 100.)); let txt = g (H.CoerceTo.input (g (d##getElementById (s "todo_text")))) in let btn = g (H.CoerceTo.input (g (d##getElementById (s "todo_button")))) in let ul = g (d##getElementById (s "todo_ul")) in let rec handler items = (fun _ -> let v = txt##value in let new_items = items @ [v] in Firebug.console##debug (s ("DoNe adding: " ^ Js.to_string v)); let li = H.createLi d in li##textContent <- o v; Dom.appendChild ul li; btn##value <- s ("NewDoNe#" ^ string_of_int (1 + List.length new_items)); txt##value <- s ""; btn##onclick <- H.handler (handler new_items); Js._false) in btn##onclick <- H.handler (handler []); Js._false)
- すでに別の人も指摘しているとおり、以前の「状態渡し」のほうがOCaml上のGUIアプリの実装としては異常。書けないとか言うから反例として書いただけ。OCamlは非純粋関数型なので、普通は副作用(破壊的代入を含む)を使う。FRPを含め、純粋関数型のGUIプログラミングがしたかったらHaskellのライブラリなりElmなり、それ用の適切な道具を使えば良い。
- 自分から要求した「お絵かき」の「トイプログラム」すら1年もかかって自称FRP(実際にはFunctionalですらない命令型プログラム。哲学とか時刻とか理屈をこねたり過去の値を保存したところで、ユーザから見て命令型の非単一代入であることに何ら変わりはない。ちなみにtimeengine.jsの保存のしかただと通常のFRPと異なりたとえ「過去の値」が不要になってもJS処理系がガベコレできないのでメモリリークします。)でしか書けないのに、また他人に実装を要求する正当性は1ミリもない。
2015年7月7日火曜日
お絵かきロジック(笑)の続き
元の話題をすり替えて「カクカクしている」(直線しか引けない、と言いたいらしい)とか難癖をつける奴がいたので、また自分の確認・練習も兼ねて。Firefox推奨。
open Dom_html open Graphics_js let () = (* open_graph "" *) Js.Opt.iter (CoerceTo.canvas (getElementById "canvas2")) Graphics_js.open_canvas let rec k = function | `Up -> loop [Button_down] (fun status -> moveto status.mouse_x status.mouse_y; k `Down) | `Down -> loop [Button_up] (fun status -> k `Up); loop [Mouse_motion] (fun status -> lineto status.mouse_x status.mouse_y; k `Down) let () = k `Up後半は
let rec k () = loop [Button_down] (fun status -> moveto status.mouse_x status.mouse_y; loop [Button_up] (fun status -> k ()); loop [Mouse_motion] (fun status -> lineto status.mouse_x status.mouse_y)) let () = k ()でも良いはず。わかりにくいからやめておくが。
登録:
投稿 (Atom)