標高+1m

Don't be rational.

場を使った相対論的オブジェクト間コミュニケーション

アクターやオブジェクトにおいて採用されているメッセージング機構(つまり宛先アドレスを持ったメッセージ)は、自然界のシミュレーションやIoTデバイス間コミュニケーションに適さないのではないか、という仮説のもと、メッセージングに依らないオブジェクト間コミュニケーションについて考えています。ここまでの経緯は、以下2つの記事を参照してください。

VOC (Vision Oriented Communication)の中心にあるコミュニケーション機構は、以下の2点に集約できる。

  • 情報は自身を中心に波状に拡散する
  • だれでも視野の中にある情報は読める

f:id:ympbyc:20151113195651p:plain

前回は、場(場 - Wikipedia)を使った機構を紹介し、同時にこの方法のパフォーマンス上の問題点について示唆した。現状の問題点は以下の3点。

  • 全てのオブジェクトが絶対座標を持っていてかっこ悪い。
  • 場を愚直にグリッドとして実装すると、大量の無駄な計算が生じる。
  • 情報が1フレームで場全体に拡散されるのは、光速を超えた情報伝達であり、相対論に違反している。

今回はこの3点を解決する実装を紹介する。

座標を廃止し、各オブジェクト間の距離を表明するグラフを導入する。

(make-graph
  (list sun cat1 100)
  (list sun cat2 200)
  (list cat1 cat2 150))

'(オブジェクト オブジェクト 距離) でエッジを作っていって、逆引きできるデータ構造を組み立てておく。

2点の絶対座標の差から距離を出すのはコストがかかる上に、絶対座標を持つモノのネットワークは回転について余分な情報を持っている。グラフを使った地図表現に変更することで、計算コストと、不要な情報を削減できた。

真空(オブジェクトがないところ)では場は値をもたないものとし、オブジェクトの周りの小さな領域だけ計算する。

オブジェクトとオブジェクトのコミュニケーションのために、巨大なグリッド(マトリクス)を計算するのは効果対計算コストが高すぎる。物理シミュレーションをしているわけではないので、伝播方法の詳細は無視する。後述の参考実装では、field−value(場の値)として各オブジェクトにスロットを持たせ、物理が情報をそこに書き込むようになっている。

距離と伝播速度から遅れを計算し、正しい順序で情報を伝播させる。

;;例 - 光速の1/3の速度の犬の鳴き声:
`(sound "bow-wow" ,inverse-square ,(/ 1 3))
;;-> 1m以内にネズミ、3m以内にネコがいた場合
`((,cat  :sound "bow-wow" 9)
  (,mice :sound "bow-wow" 3))
;;猫には9フレーム後、ネズミには3フレーム後に鳴き声が届く.

オブジェクトが場を振動させると、前述のグラフから近くのオブジェクトをそれぞれ取り出し、しかるべき時間の遅れののちにターゲットオブジェクトが監視しているローカルな場に、波の減衰曲線を距離に適用して得た値を書き込む。

[場の振動命令]は、減衰曲線(典型的な例は距離の逆2乗)と、光速(=1)に対する比率の形の伝播速度を付与されている。

参考実装

静止系のVOCネットワークを実装してみた。プロトタイピングでリストを乱用していて読みづらいのが恐縮ですが、コンセプトは大体表現できたと思う。

場を使った相対論的オブジェクト間コミュニケーション · GitHub

前回の記事と同じく、一つの太陽と2匹のネコの静止系で実験してみる。

f:id:ympbyc:20151113211141p:plain

(define (main . args)
  (let ([cat1 (make <cat> :fields '(sound temperature)
                    :body-temperature 34)]
        [cat2 (make <cat> :fields '(sound temperature)
                    :body-temperature 34)]
        [sun  (make <sun> :fields '(temperature electromagnetic)
                    :temperature 1000000)])
    (run-space (list cat1 cat2 sun)
               (make-graph
                (list sun cat1 100)
                (list sun cat2 200)
                (list cat1 cat2 150))
               800)))

結果:

f:id:ympbyc:20151114134200p:plain

時刻100: まず、太陽からの電磁ノイズがピンクの猫に到達する。ここでは光速を1として計算しているため、距離がすなわち時間の遅れとなる。同時刻に1000000℃あった太陽熱が、距離の逆2乗(100℃)まで下がってピンクの猫に到達し、猫は "too hot meow!" と鳴く。

時刻200: 100フレーム後には、太陽からの電磁ノイズと熱が緑の猫に伝わる。熱は減衰して25℃になっていて、気持ちよいポカポカ陽気なので、猫は "nice and warm meow!" と鳴く。

時刻400: 時刻100に発信され、光速の1/3で伝わる音波"too hot meow!"は、300フレーム後に太陽に到達し、太陽の周りの音場を揺らす。ここでは音については厳密に減衰を考慮していない。

時刻550: ピンクの猫が発した鳴き声が緑の猫の耳に届く。

時刻650: 緑の猫の鳴き声がピンクの猫の耳に届く。

まとめ

場を使ったオブジェクト間コミュニケーションというややこしそうな仕組みの割に、簡単に宣言的にオブジェクトを記述できるフレームワークを作れそうだ。

VOCの拡張 - 場の導入

昨日の記事でちらっと出てきた、VOCについて。1年半前にこんな記事を書いた。

ympbyc.hatenablog.com

VOC(Vision Oriented Communication)を初めて紹介した記事だが、それ以来VOCについてたまに考えていて、よりわかりやすい説明を思いついたことと、IoTへの応用を見据えた再定義を行いたいので、この記事をもってVOCの定義を改訂する。

メッセージは波であり、宛先は持たない

f:id:ympbyc:20151111224650p:plain

電話やメールと言ったテクノロジーの助けがないプリミティブな世界では、真に1対1のコミュニケーションは(接触以外に)あり得ない。

  • 眼はおおまかに400nm~800nmの波長の電磁波を受信して検波する。電磁波は波であり、ある程度の指向性こそ持たせられても特定の宛先を持つものではない。
  • 耳も音波について同様である。
  • 口はAM+FM変調した音波を送信する。
  • 身振りは可視光線を反射するパターンを変えることで、情報を光にエンコードする。

前にも言った喩えだと、信号が車のbrakeメソッドを呼ぶわけじゃなくて、ドライバーが信号を見てブレーキを踏むか否か判断する。

宛先を持ったメッセージが電話なら、宛先を持たないメッセージはアマチュア無線だ。

空間は場だ

波は場を伝わる。場は全ての地点になんらかの値を割り振られたグリッドと考える。場は、音場、電磁場等自然由来の場を使ってもいいし、シミュレーションでもいい。例えば文字列が値となる場があったって良いし、音場に方向を追加してベクトルにしたっていい。場をコミュニケーションの媒体として使う。シミュレートする方法は問わない。

受信 -> 計算 -> ブロードキャスト -> 再帰

1年半前の記事ではVOCのコンセプトと、その実装の一例を一つの記事にまとめてしまっていた。ここでは改めてコンセプトだけを説明する。

AI

VOCを実装するモノは、REPLの亜種REBLを実装する。REPLはRead Eval Print Loopで、通常Rは標準入力、Pは標準出力や標準エラーと、限定されたストリームに対する読み書きを指すが、VOCではReadとPrintを拡張し、多対多のコミュニケーションのため、それぞれReceiveとBroadcastとする。

Receive Eval Broadcast LoopでREBLだ。

Receive (受信)

自分が持つあらゆるセンサーそれぞれについて値を解析する。

シミュレーションの場合は自分の周りの場の値を読む。

Eval (評価)

受信した情報と、自分の状態から、自分の次の状態を計算する。

Broadcast (ブロードキャスト)

Evalされた自分の次の状態に応じて、影響を与える場を振動させる。「場を振動させる」っていってもそれほど難しく考えないこと。ようするに近くの誰にでも受け取れるメッセージを送り出すってことだ。本文に宛名は入ってもいいが、他のモノが見るのを妨げることはない。

Loop (再帰)

また受信待機に戻る。

アクターとの違い

VOCは実際Erlangのアクターによく似ている。違いはメールボックスの代わりに場を読み、メッセージを特定のプロセスに送る代わりに特定の場を振動させるという2点だ。

場のシミュレーションについて

実際のところ、場のシミュレーションは愚直にやると無駄が多いと思うので、実装方法は処理系に一任する。モノがある場所の値だけ計算すればいい。時間(光速度や音速)についてもシミュレートするのが理想だがなくて困らないならいらない。

Pseudo Code

改訂版VOCの実装はまだないので、ナイーブな疑似コード。

100℃の太陽と猫2匹の世界

;;気温がちょうど良いとニャーと鳴く(音場を揺らす)猫
(defn cat-with-no-mass [fields cat]
  (let [temp (read-field :temperature (:address @cat) fields)]
    (vibrate-field fields :sound (:address @cat) inverse-square
      (cond
        (> temp 30)
        "too hot meow!"
        (< temp 15)
        "too cold meow!"
        :else "warm and nice meow")))

;;温度場を照らす太陽
(defn sun [fields sun]
  (vibrate-field fields 
    :temperature 
    (:address @sun) 
    inverse-square 
    (:intensity @sun)))

;;減衰曲線を使って場を計算する
(defn vibrate-field [fields layer address decay val]
  ;; ...omitted...
  fields)

;;メインループ  -- とりあえず相対論は無視して絶対時間
(defn run-space [things]
  (loop [fields]
    (recur (merge-fields (pmap #((:ai %) fields (:property %)) things))))

;;初期設定
(run-space
 #{{:ai cat-with-no-mass
    :property (atom {:address [0 0 0]})
   {:ai cat-with-no-mass
    :property (atom {:address [100 100 100]})
   {:ai sun
    :property (atom {:address [10 10 10] :intensity 100})}}}
 #{:temperature [[[...]]]
   :sound       [[[...]]]})

シミュレーションと、IoTデバイスのネットワーク両方に同じコードが使えるとアツい。

本当はアドレスと、グローバルな場をなくしたい。場のバケツリレー(相互作用するローカルな場)とか、いくつか案はある。

とりあえず以上です。

IoTの意味がわかった

f:id:ympbyc:20151016161744j:plain

注) IoTの定義は知らないので、この記事で言うIoTは、「僕が考えるIoT」です。

最近まで気にしてこなかったけど、電子工作を始めてから興味が湧いてきたIoTについて。

現状はビーコンとかセンサーとかRFIDとかの弱いコンピュータ群とインターネットが作るネットワークを指してIoTと言っているように見える。

これでも十分面白いんだけど、今は過渡期の初期段階で、実はもっと面白いことが起ころうとしてるはずだ。

僕らにはどうしてもコンピュータは高価なものっていう固定観念があって、やっとビーコンやArduinoを惜しげも無くばらまけるようになってきたかどうかといったところだ。

本当は全てのノードが強いコンピュータになって初めて面白くなってくる。IoTは、「安いマイコンをばら撒こう」ってことじゃなくて、「汎用チップやモジュールがどんどん安くなるよ」って話なんだ。

強いコンピュータ、弱いコンピュータと言っているのはLinux搭載か否かとかではなくて、おおまかに

の全てを備えたものを強い、それ以外を弱いと表現している。BLEでデータを一方的に送信するビーコンやセンサは弱いし、wifiシールド等を付ければArduinoも強くなり得るということ。

強いコンピュータのネットワークは、OOP(メッセージングのOOP)やアクターモデルVOCの計算基盤になる。

つまり、大雑把に言うと実世界がコンピュータになるということ。

以前僕は、「OOPでは机に高さを訪ねると答えが返ってくるが、現実では僕らが自分で測らないといけない。OOPは実世界のシミュレーションには適していない。」という旨の主張をしていたが、どうやらOOPが現実に即するのではなく、現実がOOPに即するようになるようだ。じきに僕らは机と会話するようになる。

インターネットにつながる一つのデバイスを作ってIoTとか言ってちゃだめで、大量のボードを惜しげも無く使ってネットワークを構築して、ネットワーク単位でプログラミングするのがIoTなんじゃないだろうか。

というわけで、これからは

なんかが流行ると思います。みなさんも研究してみてはいかがでしょうか。

それから、実世界ではアドレスを持ったメッセージというのは稀なので、OOPアクターモデルよりも、VOCに近いコミュニケーションプロトコルを採用する方が賢いと予想します。ようするに1対1のメッセージではなく、多対多の 受信->計算->ブロードキャスティング のループを各ノードが実装するってことです。

VOCについてはこちらを参照してください。

ympbyc.hatenablog.com

カッコの少ないSchemeを作る

f:id:ympbyc:20150908160824p:plain

昨日の記事

ympbyc.hatenablog.com

の内容をScheme (gauche)で実験できるようにしてみます。

今回の記事は、上から順にGaucheのリスナに貼り付けていけば、動作が確認できるようにしてあります。

準備

まず、固定長引数とそれ以降の引数を取って、余った引数を結果にapplyする関数を作るマクロを作ります。define-syntax面倒臭いのでdefine-macroしちゃいます。

(define-macro defix
  (lambda (defclause . body)
    `(define (,@defclause . next)
       (if (pair? next) 
           (apply (begin ,@body) next)
           (begin ,@body)))))

(macroexpand '(defix (-car x xs) x))
;;=> (define (-car x xs . next) 
;;=>   (if (pair? next) 
;;=>     (apply (begin x) next)
;;=>     (begin x)))

(define-macro lamfix
  (lambda (params . body)
    `(lambda (,@params . next)
       (if (pair? next) 
           (apply (begin ,@body) next)
           (begin ,@body)))))

(macroexpand '(lamfix (x xs) xs))
;;=> (lambda (x xs . next) (if (pair? next) (apply (begin xs) next) (begin xs)))

ちょっと汚いけどあんまり気にしないで。

以降、すべての definedefix, lambdalamfix に置き換えて定義していきます。

リスト関数

(defix (cons x xs)
  (lamfix (f) (f x xs)))

(defix (car xs)
  (xs (lamfix (y ys) y)))

(defix (cdr xs)
  (xs (lamfix (y ys) ys)))

(defix (-car x xs) x)
(defix (-cdr x xs) xs)

(defix (<: x xs)
  (lamfix (y) (cons y (cons x xs))))

(defix (list x)
  (cons x '()))

(list 4 <: 3 <: 2 <: 1 -car)           ;;=> 1
(list 4 <: 3 <: 2 <: 1 -cdr -cdr -car) ;;=> 3

mapfilter の例も同様に、 defixlamfix をうまいこと使って作っていきます。

(defix (map xs f)
  (if (null? xs) '()
    (cons (f (car xs)) (map (cdr xs) f))))

(defix (filter xs f)
  (if (null? xs) '()
    (if (f (car xs))
      (cons (car xs) (filter (cdr xs) f))
      (filter (cdr xs) f))))

(defix (prefix f)
  (lamfix (x xs) (pa$ f (cons x xs))))

(define -map (prefix map))
(define -filter (prefix filter))

(list 5 <: 4 <: 3 <: 2 <: 1 
 -filter odd? 
 -map    (pa$ * 2) 
 -map    print)
;;=> 2
;;=> 6
;;=> 10
;;=> #<closure (cons cons)>

パイプライン演算子

-> も簡単です。

(defix (-> x)
  (lamfix (f)
    (lamfix (g)
      (f (g x)))))

(defix (<- x) x)

(-> 2
 -> (pa$ + 3)
 -> (pa$ * 4)
 <- number->string)   ;;=> "20"

できました!

混ぜて使う

固定長引数の関数は defix で定義して、可変長引数の関数は define で定義すれば、混ぜて使うこともできます。

(define (list . xs)
  (fold-right (lambda (x acc) (cons x acc)) '() xs)) ;;再定義したconsを使うため

(-> (list 1 2 3 4)
 -> (cut map <> (pa$ * 2))
 <- (cut map <> print))
;;=> 2 4 6 8 #<closure (cons cons)>

まとめ

Scheme処理系の上に、マクロを使って評価規則をちょっとだけ変えたLispを実装しました。Lisp式のマクロがあると言語の(見かけ上の)評価規則すら変えられるという例です。

リストの例(consの改造)は影響が大き過ぎるので実用は難しいと思いますが、固定長引数の関数を新しく作る時にとりあえず defix を使ってみるっていうのはやってみると意外と実用的かもしれません。


オススメ

Lispから可変長引数を引き算したらできること

f:id:ympbyc:20150907225639p:plain

Schemeから可変長引数を引き算したら -- Island Life

Shiroさんが面白い記事書かれてたので、前に実際に可変長引数をなくしたLispを作って発見したことを紹介します。*1

実装者とは全く別の、遊ぶ人の視点からの記事です。

この記事では意図的にマクロを一切使わないという制約のもと、いわばラムダ計算パズルをします。

左結合のカッコを省略する

少々恣意的ですが、

((compose f g) x)

;;こう書ける
(compose f g x)

こういうこと、引数の数が固定なのでカッコを省略しても一意に評価できます。関数を返す高階関数を使うときに便利。以下の例では全てこの仕組みを存分に活用します。

list関数もクオートも使わずにリストを作る

Shiroさんの記事で言及されていたように、可変長引数がないと (list 1 2 3 4) ができなくて不便です。これも一工夫でうまいことできます。要するに1つずつ cons していけばいいって話ですが、それでは面倒なので前項の仕組みを活用します。

まず cons, car, cdrの仕組みを思い出してみます。

(define (cons x xs)
  (lambda (f) (f x xs)))

(define (car xs)
  (xs (lambda (y ys) y)))

(define (cdr xs)
  (xs (lambda (y ys) ys)))

こうですね。*2 2値にconsを適用するとクロージャに評価されます。
ここでちょっと寄り道して carcdr を少しシンプルにした -car-cdr を作ります。

(define (-car x xs) x)

(define (-cdr x xs) xs)

;;すると

(cons 1 (cons 2 '()) -cdr -car) ;;=> 2

こうやって後置のリスト関数が作れるわけですね。同じ理屈で中置バージョンのcons、 <: を作ってみます。

(define (<: x xs)
  (lambda (y) (cons y (cons x xs))))

時間がかかりましたがこれを左結合カッコの省略と組み合わせると、やりたかったことが実現できます。

(cons 1 '() <: 2 <: 3 <: 4 <: 5) ;;=> '(5 4 3 2 1)

;;おまけ

(define (list x)
  (cons x '()))

(list 5 <: 4 <: 3 <: 2 <: 1) ;;=> '(1 2 3 4 5)

まあ普通の list よりはちょっと不便ですが見た目は綺麗でしょ?

リスト関数の後置、中置化

同じように他のリスト関数も後置または中置バージョンを作ると便利です。予め引数の順番を変更したリスト関数を作っておきます。

(define (map* xs f) (map f xs))
(define (filter* xs f) (filter f xs))
;;etc

(define (prefix f)
  (lambda (x xs) (pa$ f (cons x xs))))

(define -map (prefix map*))
(define -filter (prefix filter*))
(define -take (prefix take))
;;etc

こうすると

(list 6 <: 5 <: 4 <: 3 <: 2 <: 1
 -filter even?
 -map    (pa$ * 2)
 -take   2
 -fold   + 0)      ;;=> 20

便利でしょ?

パイプライン演算子

この仕組みで中置化できるのはなにもリスト関数だけではありません。関数合成 compose を中置化してF#のパイプライン演算子 |> や、clojureのスレッドマクロ -> のような関数を作ります。例によってマクロではなくただの関数です。

(define (-> x)
  (lambda (f)
    (lambda (g)
      (f (g x)))))

(define <- identity)

(-> 2
 -> (pa$ + 3)
 -> (pa$ * 4)
 <- number->string)   ;;=> "20"

中置とは言っても最初の値をラップする仕組みのためにちょっと不思議な見た目になります。 -> で計算をつないでいって、 <- で結果を取り出します。

まとめ

  • 可変長引数を撤廃することで、左結合のカッコを省略できます。
  • するといろいろ面白いことができます。

Land of Lisp最高です。

続き: Gaucheで実験できるようにしてみました。

ympbyc.hatenablog.com

ちなみに、もうずっと触ってなくて動くかわからないんですが、実験に使ったオレオレLispはこれです。

github.com

*1:この処理系ではデフォルトのカリー化もやっていて、これを使うともっと面白いこともできるんですが今回は割愛します。

*2:実際のScheme処理系ではconsセルをクロージャで表現するということはしないと思いますがここでは忘れます。

燃え尽きプログラマと動的開発について

f:id:ympbyc:20150810204334p:plain

徳島の山奥でフロントエンドJSのための動的開発環境を作っている。関連して、会社の技術ブログでチラッと動的開発について触れた。こっちではもう少し突っ込んで書いてみる。

この記事の後半ではBirdについて触れるけれど、この記事の目的はBirdの宣伝ではなく問題提起と解決策の示唆です。一緒に開発環境について真剣に考えてみたい。

mechanic.pilotz.jp

高等言語の発明から60余年、数え切れないほどのプログラミング言語があって、みんなそれぞれ気に入った言語を使ってソフトウェアを記述しているけれど、一部の例外*1を除くと、どの言語を使っても本質的な開発スタイルは一緒なのが現状だろう。ドキュメントを参照しながらエディタないしIDEソースコードを記述し、実行して動作確認をする(テスト含む)。静的言語と動的言語の違いはソースコードの記述と実行の間にコンパイルが挟まるか否かだけだ。

これが悪いというわけじゃない。シンタックスが好きな言語なら、エディタで綺麗にハイライトされたソースを眺めてるだけで楽しいこともある。僕はRainbow Delimiterで虹色に輝くS式をPareditするだけで心が落ち着く。

でも時々、エディタと向かい合うのが辛い時がある。Joel on Software - 射撃しつつ前進で書かれているような現象で、このブログでも過去に触れた

プログラマの生産性の一番大きなファクターはその時の精神状態だ。もうこれは有無を言わさずそうなのだ。プログラミングスキルや知識やコミュニケーション能力や読解力なんかは差し置いて、ダントツで気分の問題が一番大きいのだ。管理職の方は是非参考にしてください。

話が脱線したように見えて、これがそうじゃない。気分が良い時は、お気に入りのエディタとお気に入りのキーボードでお気に入りの言語を書き殴るのが一番効率が良い。些細なデバッグに1時間や2時間かかろうとも砂糖とカフェインとニコチンで乗り切れるし、残業でも泊まりでも平気でできる。要するにハイだからだ。

でも気分が乗らない時はもうどうしようもない。エディタを開くのさえ苦痛で、画面いっぱいのソースコードなんて見た時には目は虚ろに、脳みそは粘土になって、手は勝手に⌘-tabを叩いて僕をブラウザに引き戻す。こんなときはGmailをポチポチいじったりSlackでbotをからかったりとか軽い作業しかできない。これは皿洗いとか洗濯とかと一緒で、要するに一度やり始めてしまえばどうってことはないはずなのだ。でもプログラミングはこの状態からやり始めるまでの障壁が極端に高い気がする。

だったらGmailやSlackを弄るくらいの感覚でプログラミングができれば良いんだ。

エディタで書いて、別途動作確認というループをUIとして捉えて、その問題点を考えよう。

まずレスポンスの問題がある。普通のUIなら、なにかアクションを起こしたときにアプリケーションを切り替えて最初から実行し直して結果を確認するなんてことはあり得ない。Slackで文章を書いてEnterを押したらすぐにチャンネルに表示されるし、Gmailでスレッドのタイトルをクリックしたらすぐに本文が表示される。ゲームと一緒なのだ。アクションに対する報酬は即座に欲しい。これを 課題1 とする。

次に、イントロスペクションの問題。これにドキュメントの問題も含める。実際にプログラムを書くときは、エディタ→動作確認→エディタ→動作確認という単純なループにはならず、デバッグのために実行時情報を覗き見たり、ドキュメントを参照したりといった脇道が発生する。理想的には全部一つの画面でやりたくないですか!? これが 課題2

最後に、エディタの精神衛生上の問題は、きっとその圧倒的な視覚的情報量だ。そして多くの場合、表示される情報が十分整頓されていないこともストレスの原因だろう。これを 課題3 としよう。

課題1課題2 の半分を解決するのが動的開発だ。ここで言う動的開発ってなんのこっちゃについては記事冒頭のリンク先を参照して頂きたい。簡単に言えばランタイムに開発する手法のことだ。開発環境自体が実行環境上のソフトウェアなのだ。書いた式はその場で評価して結果がわかるし、実行時情報についてはそもそもこれが実行時、つまりランタイムであるわけで、その場でなんでも覗き見れる。 課題2 の残り半分、つまりドキュメント問題は動的開発の定義の範疇ではないというだけで、動的開発環境にドキュメント参照機能をつけるのは簡単だ。

問題は 課題3 だ。これの解決には2つ必要なものがあると思う。コード分類の指針とGUIだ。

まず情報の整頓が十分でないとはどういうことか。

テキストファイルだとどうしても上下方向への1次元しかコードの各部分(コード片)を分けて配置する余地がないため、まったく違う役割のコード片がダラっと並んでしまいがちだ。加えて上下方向の次元は、純粋関数型言語でもない限り実行の順番というセマンティクスも持っているため、実行順に依存したコードなのかそうでないのかがはっきりしないという問題も抱えてしまう。

それぞれ疎なコード片は疎であることがはっきりするように並べたいわけで、これにはもう一つ以上の次元が必要になることを納得頂きたい。

コード片を分類する指針も必要だ 。冒頭のリンク先で紹介したSmalltalkでは、これをクラスカテゴリとクラスとメソッドカテゴリで分類していて、クラスブラウザを使ってナビゲートできるようになっている。

今回開発中のBirdでは違う方針を採った。だってオブジェクト指向じゃないから。

Birdを汎用の開発環境ではなく特定のFLUXフレームワーク専用にした意図はここにある。フレームワークを使うと、自然に各コード片は明確に分類できる。MVCでいえばモデルはモデルで纏めればいいし、ビューはビューで纏めればいいということだ。今回はFacebook流のFLUXを分解して、コンポーネントの概念をなくした簡素な(未圧縮2KB)フレームワークを使った。コンポーネントがないので、このフレームワーク用のコード片は

の4種類に分けられる。ちなみに関数型なのでストアはオブジェクトではなく、リードオンリーなオブジェクトへの参照(Clojureでいうatom)である。

この分類を使うと、こんなUIを作れる。

f:id:ympbyc:20150811194146p:plain

トランジションリスナの赤丸や、トランジションとDOMリスナのピルにホバーするとソースがプレビューできて、クリックすれば編集できる。一度に提示する情報が減り、圧迫感が軽減された。

コードの種類の次元が加わり、更にトランジションリスナは3次元になっている。エディタでの上下1次元の配置より格段に整頓されていることに同意してもらえると思う。

注意して欲しいのは、この記事ではグラフィカルプログラミングについて何も触れていないことだ。グラフィカルであることに主眼はない。インターフェースの改善を実直に行った結果こうなっただけ。

思ったより長くなってしまった。とにかくこれで課題は3つとも解決し、気分が乗らない燃え尽き状態でもGmailやSlackのようにプログラミングできそうな兆候が見えてきたのではないだろうか。

Birdを使った開発の様子をスクリーンキャストしてみた。お気楽な感じが出せただろうか。

www.youtube.com

Birdはまだ生焼けだけど、方向性は面白いんじゃないかと手前味噌ながら思っている。

面白い開発環境を作るのが流行ったら面白いなと思います。みなさんも如何でしょうか。

Birdを触ってみたい方はこちら

mechanic.pilotz.jp

*1:Smalltalk, データフロープログラミング, Scratchなどのグラフィカル言語, etc