Erlang実装パターン

EUnitを読みながら、こういうことかな、と思ったところをメモ。あくまでもErlangの文法書をまともに最後まで読んだことがない素人の独自解釈のメモなので、間違っている可能性もゼロじゃないです。ご注意を。はてなってErlangシンタックスハイライトも使えるのね。

アトム

小文字スタートの文字列。そのままRubyのシンボルやENUMっぽく使える。ok, errorなどは全般的に共通語として使われているっぽい。オブジェクトの属性名みたいな使い方もする。

タプル

Pythonのタプルと一緒。なんでも入れられる。入れ子にもできる。

{ok, "文字列も"}.

コレクション型を作成

sets:new().
queue:new().
dict:new().

case文の書き方

case なにか of
  true -> true時の処理;
  false -> false時の処理
end,

->の左側にはいろんなパターンマッチが書ける。

関数先頭での変数の定義

Pythonなどでこのように書く場合には、

def server():
  変数 = 何かしらで初期化
  処理...

Erlangだとこんな感じ

server_init() ->
  server(初期化した属性)

server(Param) ->
  処理

シーケンシャルな呼び出しは直列な呼び出しに変更する。

辞書検索

case dict:find(パラメータの順序不明) of
  {ok, 受け止める変数} -> 見つけた時の処理;
  error -> 見つからなかった時の処理
end.

文の区切りは3種類

Python的に言うと、インデントが終わるところは行区切りが;になる。行が次に続く場合の
行区切りは, 同名の関数定義も全部含めて終わる部分、単発の式は.で終わる。英語的といえば英語的。

入力パラメータの種類ごとに柔軟に処理をするための書き方

たぶん、書き方としてはたくさんあるだろうけど、そのうちの一つになるのかな?

server_command(引数1) ->
  何か準備,
  server_command_reply();
server_command(引数2) ->
  何か準備,
  server_command_reply();
server_command(引数3) ->
  何か準備,
  server_command_reply().

server_command_reply() -> 最終的に実行する文.

こんな感じで、入り口をたくさん、出口で別の関数呼び出しを入れる。

キューから一つ値を取り出して関数を呼び出す

queue:outで取り出す。 St#state{queue = Queue}
の部分で一つ取り出した後のキューを設定しているのかな?

dequeue(St) ->
    case queue:out(St#state.queue) of
        {empty, _} ->
            St;
        {{value, {Job, From, Reference}}, Queue} ->
            start_job(Job, From, Reference, St#state{queue = Queue})
    end.

caseの最初のマッチの部分は、取り出した値。後半が取り出した後のキュー。この式では、マッチを利用して、
タプルの中の値を変数に展開している。これもイディオムっぽい感じ。

構造体

-record(レコード名のatom, {キーとなるアトムが羅列されたタプル}).

この後は#state{キー = 値が連続したタプル}とやることで、インスタンス化する。

Djangoのテストサーバは偉い

Djangoのテストサーバは偉いです。Ctrl+Cを押すとその場でぱっと止まってくれます。でも、自分でSimpleHTTPServer, SimpleXMLRPCServerなんかを起動すると、やっぱり止まってくれません。調べると、acceptの所でロックしてしまっていて、Ctrl+Cで割り込みをかけても、他からアクセスが来るまではロックしていて、アクセスがきたらブレークする(そのアクセスは受理されず、クライアントでもエラーになる)という頭の悪さ。じゃあ、何が違うんだろうか?と調べてみました。

Server系のコードを見てみた。

django/core/managemanet.pyのrunserver()を見ると、from django.core.servers.basehttp import runして、run()にハンドラを渡しています。run()はWSGIServerを呼び出し、これのserve_forever()を呼んでます。WSGIServerも同じファイルで作成されていました。標準のWSGIServerはfrom BaseHTTPServer import HTTPServerを継承しています。そんでもって、標準ライブラリのHTTPServerはTCPServerを継承しています。

一方、止まってくれない方のSimpleXMLRPCServerはSocketServer.TCPServerを継承しています。ここで同じ親を継承しているところまで行きました。どちらも、継承
しているところではserve_foreverには手を加えていません。

Djangoの方を見ると、WSGIServerなどは、すべてserver_bindをオーバーライドしていて、ちょこっとbindしたときの情報を追加して、という程度の手しか加えられていません。listen周辺(server_activate)はそもそも手は加えられていないっぽい。なぞだ。該当部分はそれぞれこんな感じ。

TCPServer:
   def server_bind(self):
       """Called by constructor to bind the socket.

       May be overridden.

       """
       if self.allow_reuse_address:
           self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
       self.socket.bind(self.server_address)
       self.server_address = self.socket.getsockname()

WSGIServer(HTTPServer):
   def server_bind(self):
       """Override server_bind to store the server name."""
      # コメントが違う。チケットを切った方がいいのだろうか?
       try:
           HTTPServer.server_bind(self)
       except Exception, e:
           raise WSGIServerException, e
       self.setup_environ()

class HTTPServer(SocketServer.TCPServer):
   def server_bind(self):
       """Override server_bind to store the server name."""
       SocketServer.TCPServer.server_bind(self)
       host, port = self.socket.getsockname()[:2]
       self.server_name = socket.getfqdn(host)
       self.server_port = port

で、調べたところ以下のような記述を発見。get_requestをオーバーライドして、select.select()を使うようにするというやつです。僕はC言語を勉強したのはMS-DOS時代なので、シグナルとか、selectとか、pollとか、foldとか、そのあたりの足腰が貧弱なんですよね。勉強になりました。割り込みベクタとかint 21hぐらいしか分からない子なので。

http://d.hatena.ne.jp/odz/20061125/1164435020

でも、grepしてみても、selectは使っていませんでした。謎は深まるばかり。

autoreloadが犯人か!?

ためしに、socket.acceptをしているところに、raise ValueError()を埋め込んで実行してみたら、どうやらDjangoのWSGIServerは別スレッドが作られてその中で実行されているみたい。そうか、スレッド使えばKeyboardInterrupt受け取れるのか?と思ってスレッドを使って書き直したけど、やっぱりNG。

django/core/managemanet.pyのrunserver()をよく見ると、autoreloadが有効でないときはそのままWSGIServerを実行し、そうじゃない時はautoreloadに処理を渡して、そのなかから実行している模様。試しにautoreloadをオフにしてDjangoのテストサーバーを実行してみると・・・ビンゴ!Ctrl+Cでは止まらない、行儀の悪いサーバになりました。autoreloadに秘密がありそうだ。

django/utils/autoreload.pyを見てみる。何このコード。意味不明。spawnveを使っている。なるほど。autoreloadはこうやって実装されているのか。最初に、restart_with_reloaderがよばれて、spawnveする。その後生成される環境はRUN_MAINがtrueなので、start_new_threadが呼ばれる。つまり、別のプロセスになってしまえば、親の方はKeyboardInterruptは使えるということかな?その場合に、子供環境も終了させられるのか否か。ここまではチェックしきれずに終わりました。

という感じでダラダラと調査メモ。まとめたらブログにかく。便利なコードはスニペットのサービスに書く。

PySpec機能拡張案

案ばっかり出てなかなか進まないけど〜

  • HTMLドキュメント生成機能をコンソール版にもつける
  • MockObjectの機能強化。with_any_argumentsとかね。呼び出し回数を柔軟に設定できるように、とか。
  • GUIテスト対応
  • WSGI対応して、ウェブアプリのテスト機能対応。

簡単なMockあたりはヒマなときにさくっと終わっちゃいそうだけどね。HTMLはどうしようかなぁ。

英語チュートリアル

英語チュートリアルを作成中。チュートリアルを作成していると、「あぁ、こういう機能も欲しいね」と思いつきます。自然言語を書くのも貴重なインスピレーションの元ですね。開発前のソフトウェアが形になっていないときの仕様書なんて、いくら書いても地に足が着かない状態だので効率は良くないとは思いますけどね。

今のところ考えているBDDの手順は以下の通り。

  1. 仕様を自然言語で書いてみる
  2. 仕様をpyspecを使って表現してみる
  3. pyspecを実行してみる→失敗
  4. pyspecが通るようにコードを書いてみる→パス
  5. 製品コードをリファクタリング→テスト
  6. 仕様コードをリファクタリング→テスト

基本はTDD。フィードバック回数、頻度が高ければ高いほどいいけど、そこはもうTDDでやり尽くされている領域なので、あえてそこを離れる必要はないしね。TDDとの違いで言えば、コンテキストと仕様を明確に分ける、ということ。最後に、仕様コードもリファクタリングしてから終わるということ。本当はHTMLでドキュメントが生成できれば、最後の段階でマニュアルもできるので、もうちょっと良くなると思うけど。

DELLとトヨタの比較

DELL世界最速経営の秘密

DELL世界最速経営の秘密

DELLの本を読みました。在庫削減はトヨタ生産方式の代名詞ですが、トヨタ生産方式の大野本を読んだときとだいぶ印象が違いました。顧客のために、という理念を達成するためにダイレクトモデルに絞り、在庫を減らす、という話に繋がっていきます。同じ在庫を減らすのでも、トヨタは戦後の製造業を維持していくためには生産力の急激な向上が不可欠、という信念があったと大野本には書いてありました。その価値観はもはや通用しないので、手段と目的があべこべになって、「××の闇」本がいっぱい書かれたりする自体になっているのかなぁ、という気がしています。

この本を読んで一番大きな気づきは、デルが急成長している企業なのに、サプライヤーと一緒に成長していくだとか、顧客の利益などの、企業文化を大事にしている、ということです。Joel On Softwareには、急成長している企業には文化を構築する余裕がない、と書いてあったので、異色な感じがしました。企業を維持拡大していくためには、不変の価値観が必要。この本からはそれが読み取れます。

注文が入ったら、サプライヤーにもその情報が伝達される、という仕組み。在庫削減をするには当然必要となる仕組みだろうなぁ、と考えてきたことがここに書かれていました。トヨタ生産方式の教科書には「カンバンで情報が伝達される」のように書かれていますが、さすがに21世紀にもなって、それを馬鹿正直にやっているなんてことはないよなぁ、と思います。おそらく、デル同様、サプライヤーも含めた情報伝達の仕組みを現在のトヨタもやっていると思います。カンバン原理主義者はそうは思ってないかもしれませんけど。

生産革新、人事、企業文化、技術とのつきあい方。デルにまつわるいろいろな話が展開されていきますが、一本の芯が通っているので、読んでいて気持ちが良かったです。それはそうと、タブレットPC,もっと安くならないのかな?10万円代にならないとまだ手が出ません。

PySpecバージョンアップ

pyspec。地味にバージョンアップ中。でも公開場所がないので。自分のマシンの中だけ。codeplexにでも場所を作ろうかな。現在のバージョンは0.50。

今日はpyspecにユーティリティクラスを追加。デリゲータなんだけど、複数のオブジェクトにメッセージをブロードキャストできるの。これで TestResultを複数登録できるようにする予定。コンソールに出しつつHTMLを出力する、とかね。HTML出力を作らないと。

Design by Contractの基本仕様は完成。場合によってはpyspecから切り離して、単独でも使用する可能性があるので、コアは小さく。後は、このメソッドの実行結果を拾って、レポート出力ができれば動く仕様書にまた一歩近づくかもね。

オブジェクト倶楽部で侍RedさんのRejectTalksを聞いて閃いたpyspec拡張。About(XX). should_not_change()。一度目に実行したときの結果を覚えておいて、二度目以降はそれと比較するの。大規模レガシーシステムのテストを手早く作る時専用の非推奨機能。あの資料もう一度見てみたいなぁ。その前に自分の資料も公開しないと。