#a -> (M b -> M c) って感じで型を共通にしてる「部品」があって、その部品にちょこっと機能を付け加えたい (たとえば最終出力に c -> d なる関数を噛ませて出力をちょっと加工したい) っていう時、「そういう機能を付け加える操作」を綺麗にくくり出すにはどうすればいいのかな。
#具体的には、今rfc.httpのAPIをもっと汎用的にしようという見直しをしてて、
#http responseを受け取るのに、今までの:sinkと:flusherではなく、手続き (StatusCode,[Header],TotalLength) -> ((Port,Size) -> a) というのを渡すように変えた。
#responseのヘッダまでを受け取った時点でこの手続きが呼ばれ、返される手続き (Port,Size) -> a でもってresponse bodyを処理する。Portは入力ポートでresponse bodyを読み出す。Sizeは読むべきデータの大きさ。
#chunked transferがあるので、(Port,Size) -> a は複数回呼ばれ得る。Sizeが0で呼ばれたらbodyの終端に達したということ。最後の戻り値がhttp-getなどの戻り値に使われる。
#そうすると、例えばbodyを全部文字列にして返すやつは
#(define (http-string-receiver)
(lambda (code hdrs total)
(let1 sink (open-output-string)
;; TODO: check headers for encoding
(lambda (remote size)
(cond [(= size 0) (get-output-string sink)]
[(> size 0) (copy-port remote sink :size size)])))))
#となるし、従来のsinkとflusherを使ったプロトコルは
#(define (http-oport-receiver sink flusher)
(lambda (code hdrs total)
(lambda (remote size)
(cond [(= size 0) (flusher sink hdrs)]
[(> size 0) (copy-port remote sink :size size)]))))
#と書ける。また、直接ファイルに落としたければ
#(define (http-file-receiver filename :key (temporary #f))
(lambda (code hdrs total)
(receive (port tmpname) (sys-mkstemp filename)
(lambda (remote size)
(cond [(= size 0)
(close-output-port port)
(if temporary
tmpname
(begin (sys-rename tmpname filename) filename))]
[(> size 0) (copy-port remote port :size size)]
[else (close-output-port port) (sys-unlink tmpname)])))))
#など。
#まあ、(Port,Size) -> a のところが副作用目的で複数回呼ばれるようになってるあたり、関数的じゃないんだけれど。
#これだと、例えば「ステータスコードを見てresponse bodyの扱いを変える
#」というような処理も書ける。
#で、冒頭の疑問に戻るんだけど、例えば「全部文字列で受け取った後、そいつをちょちょいと加工してresponse bodyとしたい」という要求があった時に、手軽に「加工する手続き String -> a」と http-string-receiver :: (StatusCode,[Header],TotalSize) -> ((Port,Size) -> String) とをくっつけたいなあと。
#後ろにくっつける関数を書けなくはないんだが、どうも綺麗にならない。
#(define (http-concat-receiver sink post-process)
(lambda (code hdrs total)
(let1 retr (sink code hdrs total)
(lambda (port size)
(if (= size 0)
(post-proces (retr port 0))
(retr port size))))))
#やっぱり部品の組み方が関数的じゃないとこが裏目に出てるのかな。
#パターンとしてモナドっぽい感じがするから、なんかうまい方法がありそうな気もするんだよねぇ。
#post-process からみて、「次のチャンクをください」といったら次々に (Port, Size) を返してくれるような関数がうまいこと定義できればいいということ、でしょうか?
#それが、素直に書くなら、チャンクを受け取るhttp-get等の方が主導権を持っているので、(lambda (port size) ...) の部分はあくまでチャンク毎にコールバックされる受動的な関数になっちゃうんですね。
#シェルのパイプみたいにつなげて、データが揃うまで待たせられれば簡単なんですけど。
#なんか、コルーチンみたいなかんじで、実際には受動的な関数なんだけど、あたかも主導権を握っているように記述できる、みたいな……?
#ふむ。コルーチンにする手はあるかなあ。(lambda (code hdrs total yield) ... (let loop () (receive (port size) (yield) ...)))
#これだと、関数から戻ってくるのは結果が全部揃ってからになるから、後処理を付け加えるのは簡単だね。
#kahua.elでリモート側のkahua-shellのインストールパスとローカル側のインストールパスが異なるとkahua-shellをリモート起動できない問題、起動時にリモート側のpathを聞くようにしました。stable1_0にコミットしました。
#EmacsLisp分からないなりに書けるのが不思議。本題はレンタル鯖に仕掛けたお問い合わせフォームのバグフィックスなのだった。その手前で時間を食ったぜ。
#あ、えんどうさん、LL のチケットって来週から発売?
#まだ発表になってませんが6/1ですね。今回はローソンチケットです。
#Kahuaでクライアント側のaccept-languageって参照できるんだっけ? kahua-context-refあたり?
#なんかJavaScriptでDOMをいぢった方が早い気がしてきた
#どうせコルーチンつかってinversion of controlをやるなら、virtual portを使って受け側はとにかくEOFまで読めばいい、ってする方が綺麗なような気がしてきた。
#うーん。kahua-shellやkahua-adminのフルパス聞くようにした方がいいんじゃないかなぁ。というかそうしたような記憶があるんだけどきっと気のせいだな。
#ついでに、doc-stringも直しといてくださいな >eyasuyuki
#なってないな。slimeみたいに、C--つけてrun-kahua-shellやrun-kahua-adminがコマンドのフルパスを聞くようにした方がスマートだろうなぁ。
#これでいいのかなあ (defcustom kahua-command-path "##bindir##" "*Path of Kahua commands." :type 'string :group 'kahua)
#ま、stableはいっか。
#これって、kahua-shell-commandとかkahua-admin-commandの値は、/を先頭に置かなきゃならないことにするの?
#EmacsLispにおけるbuild-pathみたいなものがよく分からなかったので/から始まる前提です>kahua-shell-command
#うはー
#expand-file-name使えば出来るんじゃないかと
#(expand-file-name kahua-shell-command kahua-command-path)
#まぁ無条件でやるとkahua-shellをPATHから引かせたい時困るから、そこは工夫が必要かもだけど、まぁstableならkahua-command-pathにnil渡しちゃいけないことにすれば済むかな
#http://bit.ly/bQY2ri このへんですか。(expand-file-name "foo") => "/xcssun/users/rms/lewis/foo"これで良さそうな気もする #そうかもしれないが、Mapはあって良いと思う RT @cametan_001 : と色々言い訳してみるテスト。Schemeが仕様としてハッシュ持たないのは、「破壊的操作前提のデータ型」ってのを持ちたくないからなんじゃねえの?って言ってみるテスト。んで、多分その方針は正しい。
#非破壊的なMapもO(log n)で作れるし。どっちかというと、等価性判定の面倒さの方が引っかかってる気はする。
#(ここで言うMapはデータ構造としてのMapのこと。念のため)
#RT: shiro: そうかもしれないが、Mapはあって良いと思う RT @cametan_001 : と色々言い訳してみるテスト。Schemeが仕様としてハッシュ持たないのは、「破壊的操作前提… http://bit.ly/cDs9yv (via ) #RT: shiro: 非破壊的なMapもO(log n)で作れるし。どっちかというと、等価性判定の面倒さの方が引っかかってる気はする。 http://bit.ly/a39syb (via ) #RT: shiro: (ここで言うMapはデータ構造としてのMapのこと。念のため) http://bit.ly/cb5yjr (via ) #そういやR6RSにはhashtableはあっても非破壊的Mapは無いんだな。まあhashtableは先行srfiがあったからかもしれないけど、immutableな方向を向いているのにちょっとバランス悪いなって感じはするね。
#RT: shiro: そういやR6RSにはhashtableはあっても非破壊的Mapは無いんだな。まあhashtableは先行srfiがあったからかもしれないけど、immutableな方向を… http://bit.ly/9SVS8A (via ) #「VBのランタイム入れてくれ」とか「JRE入れてくれ」とかいうのも普通だったわけで、「Lispのランタイム入れてくれ」がそう多大な要求であるようにも思えない RT @cametan_001 鑑みると、アプリ配布、って事をさ。ソースレベルで考えたら「SBCL入れてくれ」とか言っても事実上、大して差はねえのかな、って思っちゃった。Lispのほうが「この処理系が必要です。これをインストールしてくれ」って言い方のほうが今後むしろシンプルなのかな、と。 http://twitter.com/cametan_001/status/14965530052 #VB(笑)。まあ、そうですね。 RT: shiro: 「VBのランタイム入れてくれ」とか「JRE入れてくれ」とかいうのも普通だったわけで、「Lispのランタイム入れてくれ」がそう多大な要求である… http://bit.ly/bEuuF1 (via ) #@eyasuyuki それだと、カレントディレクトリからの相対パスとしてしか扱われない気がするが、change-directoryするの?
#関係ないが、(び)さんのところは今、昼くらいかな。ネット事情はどんな感じ?
#前にLondonとPaphosに行った時に使ったホテルはどこも部屋にネットが無かったんだけど、ヨーロッパの事情はどんなもんかと思って。
#今のところ、全ての宿泊施設でWiFiが普通に使えますね
#バルセロナのホテルだけ有料でした。あとは無料で使えてます。
#あと、ソフィア王立美術センター(マドリー)にも無料の無線アクセスポイントがありました。
#ネット環境はけっこう充実してると思います。
#スペインは、ホノルルとは12時間ずれですね。
#いいな。USだと都市部のビジネスホテルでもうっかりすると部屋からつなげなかったりするからな (ロビーにwifi、というのは多い)。
#ベルリンのホテルがそうでしたね。というか部屋には有線LANがあったんだけど、なぜかつながらず、結局ロビーに降りて有料のWiFi買ったという馬鹿っぷりだったのですが。
##むぅぅ。virtual portのハンドラはCからのコールバックなので、継続によるコルーチンをうまく動かせないなあ。いやな制限だが、取り除くにはScm_Read/Writeとportまわりを全部継続渡しに書き換えるしかない…
#Schemeから呼ばれるぶんについては全部Schemeで書き直してがんばってチューニングする、という可能性も無くはないが、例えばCのルーチンからScm_Getcを普通に呼び出したい、という場合に、その向こうが継続渡しで書いてあると一度user_eval_innerを通らないとならない。このオーバヘッドは馬鹿にならない。
#Scm_Getcのうち難しくなるのはバッファが空になった時だけだから、軽いパスではuser_eval_innerを通らないようにできるか? それならまだ可能性はあるか?