レガシーコード生産ガイド

私に教えられることなら

過去の記事を消した・コメント欄を閉じた

いろいろ向き合い方を変えた。

過去の記事を消した

考え方が結構変わりやすく、また昔の記事を読み返して「幼稚だな」と恥ずかしくなることが多々ある。

かなり自分の意見を書きたい欲があるんだけど、残ることを考えると迂闊なことを書けないな、と躊躇ばかりするようになった。

というわけで、記事の内容に関係なく、新しい記事を書いたら同じ数だけ消していくことにした。

いろいろ悩んでて全然成果が出せなかったときに、過去の記事を読み返して自分の成果を確認し、やる気を出そうとしてたこともった。しかしもう、そういったことは起きない、ずっと進み続けられるだろうと確信したので消す。まあそれか消した事を後悔するか、どっちかですね、どっちか。

コメント欄を閉じた

併せてtwitterやブックマークなどの反応も一切見ないことにした。もう殆ど見てなかったんだけど。はてなスターだけは「良い」という評価だけなので糧にします。

そうやって批評に耳を貸さないと自分の間違いを認められず人間的に成長できないのでは、という考えは間違いだと思うようになった。成長には関係ないしデメリットしかない。

まず間違いかどうかが客観的にわかるタイプの事について。例えばプログラミングの知識などだけど、これは間違ったまま覚えてるといずれバグなどに出会って修正されるだろうし、出会わなければ修正する必要もない。面と向かって指摘されると自分は素直に受け入れられるタイプなので問題無いと判断した。間違った記事を書いてしまっても、どんどん消していくので問題は無い。鵜呑みにした人が悪い。

次に客観的に間違いかどうか判断できないタイプの事について。政治信条とか。

これはツイッターを見てると、批判されると狂っていく、としか思えないようになった。自分の意見を肯定する発言を探して同調し、反対意見には耳を貸さず、どんどん先鋭化していく人しか見かけない。確証バイアスや後知恵バイアスの存在を考えると、誰でもそうなるのが普通で、素直に主観的な自説を疑ったり主観的な反論を受け入れる人はどこかちょっとおかしいのかもしれない。(客観的に検証できるものは別)

もちろん自分も例外では無いはず。誰かが見てると意識すると、一貫性を保とうとして間違いを認められず、自分が正しいという証拠や考えを探しそう。

それから、自分を肯定する「意見」も、それはそれで害になりそう。よっぽど親しくない限り、程度の多少はあっても、期待に応えることを自分の意見より優先してしまう、と自己評価している。スターがちょうどいいですね。

というわけで、できるだけ精神の柔軟性を保ちたいので、反応などは一切見ずに好き勝手書いていきます。

近況とLateBinding

DenLispでVirtualDOMライブラリ等ができたので、日夜破竹の勢いでアプリケーションを書いている。

できるだけLateBindingを意識したので、一部の変更で全体を再コンパイルしたりする必要はない。例えばある関数だけをコンパイルすればそれを呼び出す関数の動作も変わる。

しかしやっぱり、設計によっては、モジュール全体の再コンパイルやVDOM全体の再描画を明示的に行う必要になることも多々ある。

考えを記録しておきたいのでDenLispでの例を載せる。世の中に自分しかユーザがいない言語だけど、まあClojureとほとんど同じなのでそういうアレで……

例1 値の参照

なんかhtml的なやつを配列で返すと上手い具合に表示してくれるとする。

;; FooViewモジュール内
(def accent-color "#cca")

(def view [:div {:color accent-color} "hello"])

(def component view)

;; 上記を使う別のモジュール内
(defn render []
  (vdom/render FooView/component))

で、accent-colorの値を変更して定義しなおしたとする。

しかしFooView/viewの値は変わらない。何故ならFooView/viewの定義時にaccent-colorは参照されて"#cca"を返している。

変更を反映する方法として、関数として定義して、毎回呼び出してやる方法がある。

で、次のように変更する。

;; FooViewモジュール内
(def accent-color "#cca")

(defn view []
  [:div {:color accent-color} "hello"])

(def component (view))

;; 上記を使う別のモジュール内
(defn render []
  (vdom/render FooView/component))

まだ問題がある。今度はFooView/componentの定義時にFooView/viewは呼び出されているので、accent-colorの変更は反映されない。

よってこれも同様に関数とし、さらに使用する場所でもそれを考慮してコードを書き換える。

;; FooViewモジュール内
(def accent-color "#cca")

(defn view []
  [:div {:color accent-color} "hello"])

(defn component []
  (view))

;; 上記を使う別のモジュール内
(defn render []
  (vdom/render (FooView/component)))

結構めんどうくさい。書き換えるのやデバッグが面倒なんじゃなくて、関数に関数を重ねて……と包んでいくのが面倒。自動でそういうカンジになるパラダイムが欲しい。

例2 クロージャ

ClojureではなくClosure。生成した関数を使う場合で、こっちはもっと深刻。

以下のようなコードがあったとして

(defn adder [n]
  (fn [x]
    (+ x n)))

(defn makeIncrementer []
  (adder 1))

他の場所でmakeIncrementerで生成した関数を使っているとする。そのオブジェクトが生きているときに、例えば以下のように変更しても

(defn adder [n]
  (fn [x]
    (js/console.log "hello!")
    (+ x n)))

既に生成された関数の動作は変わらない。つまり、makeIncrementeradderを使っている場所を探し出し、その使われ方によっては再コンパイルやオブジェクトを生成する必要がある。

解決策

Smalltalkを使ってみようと思った。メッセージ送信しかなく、生成したオブジェクトの動作を後から変えられるので強そうだと思った。

さらに、前々から考えていた文法も試してみた。Smalltalkとは言いがたい文法かもしれない。例えば次のようなDenLispのコード(これもLispとは言いがたいかもしれない)は

(defn interleave [xs ys]
  let acc []
  let len (length xs)
  (loop [i 0]
    if (>= i len) acc
    (acc.push (at xs i))
    (acc.push (at ys i))
    (recur (inc i))))

次のように記述される。

interleave: xs
  let acc = [].
  let len = self length.

  loop [i = 0] {
    if (i >= len) acc.
    acc push: (self at: i).
    acc push: (xs at: i).
    recur [i inc]
  }

で、DenLispでコンパイラを書いた。中間表現をS式にして、パーサコンビネータJavaScript出力の一部はDenLispのものを使いまわしたので楽にできた。

ライブラリもある程度書いたところで、次のようなことがわかったのでSmalltalkにはしないことにした。完全に個人の感想です。

  • 設計によってはほとんど同じような問題が起きる。LateBinding度がせいぜい60%から70%に上がる程度の印象。
  • クラスオブジェクトとそのクラス等によるネットワークは、DenLispのモジュールに比べると再生成などの見通しが悪い。安心できない。
  • クラスの継承ツリーと、インスタンス←→クラスの関係、2つのグラフがcomplectされてるのは何かが絶対におかしい。何なのかわからん。
  • データが循環参照を持つのは、イベント通知などの単純な双方向リンクを除いて、概念がcomplectされている臭いを感じる。更にそれを基礎の部分に持つのは何かが絶対におかしい。何なのかはわからん。
  • 何にでも解釈されうるデータを型で包むのは概念がcomplectされている臭いを感じる。型はデータではなく動作に存在した方が好ましい。
  • S式というかEDNはとてもいい、楽。

というわけで選択肢を一つ選ばない決断ができた。ついでに(Den)Lispがますます好きになり、いくつか最適化の実験もできた。良かった良かった。

感想

最初は、やはりSmalltalkでやってみるべきか、メタクラスなどの階層はどこから実装するか、ランタイムはどうやって出力するか……などと「設計」しようとして、精神がガンガン削られていった。

アッこれはマズイぞ!と思い、まず一番チープな実装を書いて、それを一番簡単なやりかたで修正して、と繰り返した。結果、精神的には充実した状態のまま、3日ほど夜な夜な作業するだけでできた。

最近邦訳が出たアンチフラジャイルに完全にかぶれてるのもあり、プログラムの設計とか、バグを出さないための云々とか、プログラミングの勉強だとか、そういったものをかなり胡散臭く思っている。ご飯を食べたらうんちが出たから、おしりからうんちを入れるとご飯が出てくる、みたいな話だと思う。汚くてすいません。人間はimmutableでもないしコンテナ型仮想化ができるわけでもないんですよ。試行錯誤によって脳に起きる事をもっと大事にしよう。

広告を非表示にする

近況

DenLispの開発は順調に進んでいる。この勢いだとすぐ燃え尽きそうだと思ってたけど、まだ大丈夫そう。

id:lhcprさんに渡したりしてるんだけど、翌日にはIDEの見た目と機能がだいぶん変わっていたりと変更が激しい。処理系の公開はちょっと放っといて、書きだしたページやSPAだけ公開していこうかと考えている。

公開時の日記で、今後の目標としてゆっくりとVirtualDOMやデータバインディングライブラリを作ることを挙げた。今IDEのベースとして使っているVue.jsがDenLispの設計思想や作業フローと合わないので、自分で作りたい。ただVirtual DOMの実装やデータバインディングなどは難しそうなので、夜な夜な作るつもりでいた。しかし木曜の朝に「やっぱやりたいわ!」と思い立ち、木・金・土・日と4日唸ったらできた。バグの洪水で頭が茹だって倒れるかと思った。まだ本当にできたかどうかは疑っている。

f:id:phaendal:20170710002500p:plain

clojurereagentみたいなやつです。雰囲気で察してください。名前はRedon。特になんか意味ある名前ではないのでいいの思いついたら変える。

snabbdomを参考に実装してたんだけど、vdomツリーの更新された一部だけを更新したくなり、そのためには最初に生成されたvdomの変更が必要だったので、全て間接参照を挟むように変えた。

今後はVue.jsベースにDenLispで作りつつあった仕事のコード、のVue部分を置き換えていき、その過程でRedonを良くしていく。流石にエディタにはすぐは取りかからないと思う、多分……。

広告を非表示にする