塩ビ板にマザーボードなどをマウントした
今日は以前買っておいて放置していた透明な塩ビ板(5mm厚)を使ってPCケース(というか板)を作ってみました。
塩ビ板に適当に穴を開けてそこにネジを通しただけです。
拡張ボード用の穴を全部開ける前がこんな感じ。
そして完成したのがこんな感じ。ちゃんと見るとわかりますが、拡張ボードの突起が挿さるための穴が開いています。
机の横に置いて Windows8 のインストーラを起動している風景です。
ちなみに塩ビ板は10mmのジュラコンスペーサを使って挟まれており、下はネジ頭の2mm+10mm、マザーボードとの間は10mmのスペースが開いています。
また、HDDはインチネジ(自分は国内標準のミリネジに対してインチキネジと呼んでいる)で固定すべきなのですが、なにぶんインチキなのでそんなものはストックになく、無理やりM3のネジで固定しました。そのうちなんとかします。
このマシンはサーバーとしてではなく、開発用として使う予定です。そのためのグラボや電源なども今日 Amazon でポチッってしまいました。ああ、散財が激しい……。
Stream#pipeの破棄イベントの伝播
あっさりハマってメモリリークさせまくったので報告。
さっそく本題。まず図を用意。
Readable Stream -(pipe)-> Transform Stream -(pipe)-> Writable Stream
みなさんご存知のデータの流れ。しかし、問題は破棄イベントの伝播方向。
Readable Stream が閉じた場合
- Readable Stream が閉じる
- Transform Stream が閉じる
- Writable Stream が閉じる
=> 全部閉じる
よいですね。
Writable Stream が閉じた場合
- Writable Stream が閉じる
- おしまい
_人人人人人人人人人人人人人人人人_ > 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() が返すデータのサイズについて
@KOBA789 ずっと気になってるのが、ブログにあった「引数なしの read() を一度呼べば全てのデータが吸い出せるという前提」なんだよね。これがなんか stream の考え方とずれている感じが引っかかってる。そこも言及されたブログだとより良いと思います。
2013-02-11 14:37:24 via Echofon to @KOBA789
とか言われたのでちょっと考えてみたけれど、適切なバッファサイズとか流量とか、まだよくわかってない……。なのでもう少し流量制限について調べて、触ってみて、なにかわかったらまた記事書こうと思う。
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 に対する理解が深まりました。ありがとうございます!
ログをだらだら貼り付けておくので、まとめだけ読みたい人は下へどうぞ。
実際、特に違和感は無いなぁ。chunk としてオブジェクトが一つづつ返ってくることが保証されていれば、むしろその方がいいくらいな気も。
@Jxck_ そうすると、object として null を突っ込んだ時の挙動が……
2013-02-11 13:17:28 via YoruFukurou to @Jxck_
@KOBA789 あれ? null の時って objectMode になる?URL
2013-02-11 13:18:47 via Echofon to @KOBA789
@Jxck_ いや、一度 objectMode になったあとで、データとして null が来るとアウトだなー、と。null なんて使うな、という意見も一理ありますが。
2013-02-11 13:19:39 via YoruFukurou to @Jxck_
@KOBA789 データとしての null は object chunk では無いと考えると、それもそれで妥当な気もするなぁ。だって chunk に対して処理できることが無いのは変わらないんだし。
2013-02-11 13:21:19 via Echofon to @KOBA789
@Jxck_ なるほど。そうすると引数なしの read がキューの全てを返すという仕様を覆すのが手っ取り早いことになりますかね?
2013-02-11 13:22:20 via YoruFukurou to @Jxck_
@KOBA789 例えば read() でキューを全ぶ返すのを覆すとして、 buffer の場合何を返せば妥当かな?
2013-02-11 13:25:29 via Echofon to @KOBA789
@Jxck_ あー、なるほど。段々わかってきました。非 objectMode なら結合されてどこまでも 1 chunk である、みたいな考え方ですかね。
2013-02-11 13:26:41 via YoruFukurou to @Jxck_
@KOBA789 そう。つまり、 read() で全部返すのを覆すなら、同時に 「buffer における chunk とは n byte である」という defaultChunkSize が必要になると思う。そのサイズはどう決めるんだって話になるかなぁ。
2013-02-11 13:29:13 via Echofon to @KOBA789
@Jxck_ 現状の stream2 に対する考え方については理解出来ました。ありがとうございます。ただ、理解した上で、個人的にまだその仕様に反論みたいなものがあるので、再度ブログにまとめようと思います。
2013-02-11 13:36:52 via YoruFukurou to @Jxck_
@KOBA789 まあ、俺も探り探りだけど、コメント読むとそんな感じなのかなと。それ以外、やりたいことは _read() の override でやれという感じもするかも。ともあれ俺も参考になりました。 blog 期待 !
2013-02-11 13:38:53 via Echofon to @KOBA789
以下、自分の勝手な解釈。必然的に間違いを含むはず。(てか正解があるなら今すぐ欲しい感)
「書き込む」というとややこしいので、ReadableStream のキューにデータを追加することを「キューに吸い込む」と呼ぶことにする(便宜上)。
まず、ReadableStream には大きく2つのモードがある。それが、objectMode と非 objectMode。キューにデータを吸い込む際、そのデータが string, buffer, null, undefined のいずれでもない場合、自動的に objectMode になる。一度 objectMode になると二度と非 objectMode にはならない。ストリームは object に「汚染」される。
……と、ここまで書きかけて、chunk と呼んでいたものは buffer で、chunk と buffer には区別があるらしいことがわかってしまったので、もう一度まとめ直したい。
あと、コミットログ追っかけてわかったこともあるので、全部まとめ直します、はい……
追記3
リンク張ってなかったけど、続き書きました→ 続: Stream2のモヤモヤ - Write and Run