Write and Run

it's a simple way, but the only way.

**langのおべんきょしてます

勢いに任せて GolangErlang の本を買いました。前者は2冊、後者は1冊です。前者2冊はいずれも電車の中で、後者1冊は枕元に置いて寝る前に読んでます。おもしろいです。どっちかっていうと Elang の方がロマンがあっておもしろいです。でも Golang の方が低レイヤな感じがあってそれはそれで味があって楽しいです。

それだけです。おもしろいです。

塩ビ板にマザーボードなどをマウントした

今日は以前買っておいて放置していた透明な塩ビ板(5mm厚)を使ってPCケース(というか板)を作ってみました。

塩ビ板に適当に穴を開けてそこにネジを通しただけです。

拡張ボード用の穴を全部開ける前がこんな感じ。

f:id:koba789:20130325180436j:plain

そして完成したのがこんな感じ。ちゃんと見るとわかりますが、拡張ボードの突起が挿さるための穴が開いています。

f:id:koba789:20130325202512j:plain

机の横に置いて Windows8 のインストーラを起動している風景です。

f:id:koba789:20130325204344j:plain

ちなみに塩ビ板は10mmのジュラコンスペーサを使って挟まれており、下はネジ頭の2mm+10mm、マザーボードとの間は10mmのスペースが開いています。

また、HDDはインチネジ(自分は国内標準のミリネジに対してインチキネジと呼んでいる)で固定すべきなのですが、なにぶんインチキなのでそんなものはストックになく、無理やりM3のネジで固定しました。そのうちなんとかします。

このマシンはサーバーとしてではなく、開発用として使う予定です。そのためのグラボや電源なども今日 Amazon でポチッってしまいました。ああ、散財が激しい……。

Stream#pipeの破棄イベントの伝播

あっさりハマってメモリリークさせまくったので報告。

さっそく本題。まず図を用意。

Readable Stream -(pipe)-> Transform Stream -(pipe)-> Writable Stream

みなさんご存知のデータの流れ。しかし、問題は破棄イベントの伝播方向

Readable Stream が閉じた場合

  1. Readable Stream が閉じる
  2. Transform Stream が閉じる
  3. Writable Stream が閉じる

=> 全部閉じる

よいですね。

Writable Stream が閉じた場合

  1. Writable Stream が閉じる
  2. おしまい
_人人人人人人人人人人人人人人人人_
> Writable Stream しか閉じない <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

=> 源流側が破棄されない

まとめ

正直、知らんかった(バカ)。

"Stream"について考えていたらConduitを知った

Iteratee ってちょっと古かった感じですかそうですか。今更騒ぎ立ててごめんなさい。

Iteratee わけわかんねーな、run ってなんだよとか思っていたら、Conduit を見つけた。push 型から pull 型へ先祖返り、と。なんだか Stream(1) から Stream2 への移行みたいなことがこっちでも起きてるんだな。

さて、Iteratee について疑問だったことといえば、Enumeratee 同士の結合がなかったことだけど、Conduit では Conduit 同士を結合できるっぽい。ちなみに、流れ的にはこんな感じ。

Source -pull-> Conduit -push-> Sink

Conduit が左から引っ張りだして右に突っ込む感じのよう。まだよくわからん(コードも読んでないから)雰囲気だけど。

ちなみに結合則(っていうのかな?)をまとめるとこんな感じ。(相変わらずのオレオレ記法でごめんなさい)

 Source  $= Conduit := Source
Conduit =$  Sink    := Sink
Conduit =$= Conduit := Conduit

BNF を書いているみたいでとても素敵だ。Conduit を Source と結合しても Sink と結合してもいいあたり、とても美しい。やばい。

詳しくは とか Data.Conduit とか読んで俺も今から勉強します。

"Stream"について考えた(それとIteratee)

タイトルは釣り。どっちかというと Iteratee がメイン。(だってみんな Iteratee とか見ても記事開かないでしょ)

1週間くらい Stream について考え続けて、やっぱりわからなくて。でも、こんな基本的な概念、誰かが既に考えつくしてるに違いない、と思ってググってみたら、あった。そして、またお前か、Haskell

Iteratee

ほとんど Stream。モナドとかオートマトンとか言い始めるときりがないので演算子とその働きだけ大雑把に眺めてみた。

Gist: Iteratee とその演算子の働きとか。すっごく大雑把で、記号も適当ですが雰囲気だけ。

==================================================================
                Enumerator -> Enumeratee -> Iteratee
==================================================================
                                            Iteratee >>== Iteratee
                                         := Iteratee
------------------------------------------------------------------
                Enumerator $$               Iteratee
                                         := Iteratee
------------------------------------------------------------------
                              Enumeratee =$ Iteratee
                                         := Iteratee
------------------------------------------------------------------
                Enumerator $= Enumeratee
             := Enumerator
------------------------------------------------------------------
Enumerator >==> Enumerator
             := Enumerator
------------------------------------------------------------------

Stream に置き換えると、Enumerator が Readable、Enumeratee が Transform、Iteratee が Writable、という感じ。ちょっと違うけど、そんな感じ。

Stream と違うのは、Enumerator 同士の結合と、Iteratee 同士の結合。これが型と相まって地味に便利っぽい。

ということで JavaScript で Iteratee を実装してみてます。どうなることやら。

続: Stream2のモヤモヤ

追記で書くほどの分量でもなくなってきたので別にしました。

まず結論からいうと、

ドキュメントが追いついてない

だけだったようです。このコミット streams: Support objects other than Buffers · 444bbd4 · joyent/node · GitHub での変更が、ドキュメントに反映されてないようです。ドキュメントは the entire buffer とか言ってるけど、ソースコード的には objectMode のときには size 1 なのね……

If we are in "objectMode" mode then howMuchToRead will
always return 1, state.length will always have 1 appended
to it when there is a new item and fromList always takes
the first value from the list.

俺の半日を返せと。

本題

さて、謎の原因がわかったところで本題。

現状の stream2 は buffer, string, object を chunk としてサポートしている。しかし、他の2つとくらべて object は特殊。どれくらい特殊か、下の表にまとめてみた。

length プロパティ 連結の可否 任意の型のデータの格納の可否
string ある concat メソッドで可能 不可
buffer ある concat メソッドで可能 不可
object 意味ない object の連結? なにそれ 可能

圧倒的に浮いてる。同列に並べちゃいけない。それに対して array はどうか。

length プロパティ 連結の可否 任意の型のデータの格納の可否
string ある concat メソッドで可能 不可
buffer ある concat メソッドで可能 不可
array ある concat メソッドで可能 可能

これでしょ。サポートするなら。

ストリームの chunk は joinable で size(length) を持つべきだと思うんです。どこで切ってもよく、どこでつなげてもいい、そういうデータが chunk であるべきだと思うんですよ。

結論

object のサポートなんてやめて、array をサポートすべき。

とはいえ

「過去のモジュールでは object を chunk として流すものも多い。互換性はどうするんだ」とかいう反論があるかもしれない。でも個人的には stream2 は stream と決別して進化して欲しいし「歴史的経緯」なんてもので汚れてほしくない。stream1 は生かしつつ、stream2 が別に生まれるくらいでいいんじゃないかとも思う。

引数なし read() が返すデータのサイズについて

とか言われたのでちょっと考えてみたけれど、適切なバッファサイズとか流量とか、まだよくわかってない……。なのでもう少し流量制限について調べて、触ってみて、なにかわかったらまた記事書こうと思う。

Stream2のモヤモヤ

とりあえず、分かったことだけ。

正しいことを書いてるとは限らないよ!! むしろ昨日はじめて stream2 に触ったからツッコミ待ちだよ!! ドキュメントもサンプルコードも少なくて困ってるよ!! 助けて!!

3つのモード

readable stream には stringMode と objectMode、それと名前はついてないけどいわゆる bufferMode がある。で、stringMode と bufferMode はほぼ同一なのでいいとして、問題は objectMode。

ReadableStream#read([size])の挙動

read() は引数 size が省略された場合、キューの全てを返すことが期待される*1

しかし、objectMode の場合、キューの中から1つだけ返されるだけである。引数なしの read() を一度呼べば全てのデータが吸い出せるという前提でコードを書くとこの仕様にぶち殺される気がする。返り値が null になるまで読めという指摘もあるかもしれないが、キューに null を突っ込むこともできる。あちゃー。

個人的には、read(2) とかで [{obj: 1}, {obj: 2}] みたいになってほしい。つまり、キューから size ぶんだけ配列で返してほしい。そうすれば read() のときにも [{obj: 1}, {obj: 2}, {obj: 3}] になるし、中身に null が入っていても [null, {obj: 1}] となるから区別できる。

この意見の妥当性をもう一度自分で確かめられたら本家にアイデア投げてみようかなーと思う。

追記1

ReadableStream のことしか考えてなかった。pipe するときに WritableStream#write に配列が投げられたらアレゲだった。でも、結合不可なデータをストリームに流すのってどうかと思うから、オブジェクトを write するときは write([obj]) にしろ、っていうのも悪くないんじゃないかと思った。そうすれば複数チャンクを一度に書き込めるし。

まだまだ考えてみようと思う。

追記2

Twitter 上で id:Jxck さんにいろいろ指摘をいただきました。現状の Stream2 に対する理解が深まりました。ありがとうございます!

ログをだらだら貼り付けておくので、まとめだけ読みたい人は下へどうぞ。











以下、自分の勝手な解釈。必然的に間違いを含むはず。(てか正解があるなら今すぐ欲しい感)

「書き込む」というとややこしいので、ReadableStream のキューにデータを追加することを「キューに吸い込む」と呼ぶことにする(便宜上)。

まず、ReadableStream には大きく2つのモードがある。それが、objectMode と非 objectMode。キューにデータを吸い込む際、そのデータが string, buffer, null, undefined のいずれでもない場合、自動的に objectMode になる。一度 objectMode になると二度と非 objectMode にはならない。ストリームは object に「汚染」される。

……と、ここまで書きかけて、chunk と呼んでいたものは buffer で、chunk と buffer には区別があるらしいことがわかってしまったので、もう一度まとめ直したい。

あと、コミットログ追っかけてわかったこともあるので、全部まとめ直します、はい……

追記3

リンク張ってなかったけど、続き書きました→ 続: Stream2のモヤモヤ - Write and Run

*1:参照: http://nodejs.org/docs/v0.9.9/api/stream.html#stream_readable_read_size