Island Life

< ピアノレッスン85回目 | リベラルアーツ >

2013/03/11

時定数の大きな話とCL

こないだのコードの変更と型付けにちょっと関連して、向井さんが 変更の時定数が大きい実例を書かれていた。 GUIでの座標管理。これまでひとつの座標系で済ませていたところに、 複数の座標系を導入しなければならなくなった、という話。

古くて新しいGUI座標系の型の話

common-lispではいい手はあるのかな? generic functionやオプショナルな型チェックはできるだろうし、マクロを使って型チェックを導入する(しかもリリースビルドではチェックを省略するとか)できるだろう。が、そう簡単にはいかないのではないか?という気がする。どこにチェックするかを指定する、ということは、テストを書くのと同じぐらい大変だ。型レベルの変更は、コンパイラが網羅的に検証してくれるので、楽だ、というのがここでの要旨なので。

Common Lispでやるとすれば、「これまで単なる整数で扱っていたものをオプショナルに実行時型付きで扱えるようにして、クリティカルな部分から徐々に変えてゆく」みたいな手順になるかなあ。

例えばこんなマクロを書いといて:

(def-coords-macro global)
(def-coords-macro root)
(def-coords-macro display)

とする。そんで、例えばルートウィンドウの座標系を受け取る関数があったら:

(defun root-window-hoge (rx ry args)
  ... 
  (let ((gx (convert-to-global rx))
        (gy (convert-to-global ry)))
    (global-window-hoge gx gy ...)))

checking-root-coordsマクロで定義をラップする、などしてゆく。 あと座標を他の関数に渡すところをboxで包んでゆく。

(checking-root-coords (rx ry)
  (defun root-window-hoge (rx ry args)
    ...
    (let ((gx (convert-to-global rx))
          (gy (convert-to-global ry)))
      (global-window-hoge (box-global gx) (box-global gy) ...)))
  )

この段階では、checking-root-coordsマクロでラップされた関数は、 タグ付けされた座標 ((root . 100) とか) も生の数値も受け入れるので 部分的に変更していっても全体は崩さない。呼び出し側で間違えた型をつけたときだけ エラーが出る。

そんで、変更が全体に普及したと思ったら、def-coords-macro の定義を変えて、 unbox が生の数値を受けとるとエラーを出すようにする。

まあ、これもそこらじゅうにキャストをつけて回るようなものなんで、 静的型でもってそれぞれの座標を別の型にして修正して回るのと手間は変わらんのじゃないか、 と言われればその通りではある。 C++なら暗黙の型変換使えば、部分的に修正中でもコンパイルは通せるだろうし。

ただまあCLだと、型宣言とか性能計測用マクロを仕込むためだとかで こんな感じの全体を囲むマクロを作ることがちょくちょくあるので、 そういうのが既にあるところに乗っかる形でアドホックな実行時型チェックを 楽に仕込める、こともある。

もっとも現実には、こういうアドホックなマクロは締切りに迫られて 大急ぎで書かれるせいでドキュメントとかが全然無くて、 後からプロジェクトにjoinした人が暗号のようなマクロを解読するハメになって マクロ嫌いになったりするのだが。

Tags: Programming, Lisp

Post a comment

Name: