#6つのバグをチェインしてChromeのsandboxを破った話。なんつうか、岩壁にわずかなクラックを見つけて登攀してゆくクライマーって印象。 http://blog.chromium.org/2012/05/tale-of-two-pwnies-part-1.html ##「見た目良く、かつ性能も良い」、「一貫性」を成立させるために、$マクロで書けるように関数を書くというスタイルがあるのかなとか思いました。(多値を返す関数をどう組み合わせるの?ということもあるのですが。英語MLで有用な助言があったのですが私の英語力ではよく理解できていないかも。)
#$はHaskellの$を真似したもので、セマンティクスは違いますがスタイル的に似たところはあります。「軸となるデータが最後の引数になる」とでも言いましょうか。例えばtakeはリストに作用する手続きと考えられますが、Haskellではcurryingがあるので (take 2) など部分的に引数を与えた時点で「ひとつリストを取る関数」になり、 take 2 $ f $ g ... といった具合にリストを返す関数からの出力をつないでゆくように書けます。
#Schemeの場合、curryingが無いせいもあり、「軸となるデータが最初の引数になる」というのが一般的な関数のスタイルになっています。takeはリスト、個数、の順ですね。これだと、最後の引数でチェインしてゆく$と相性が悪いという面があります。Haskell風に(define (flip fn a b) (fn b a))を定義して ($ flip take 2 $ f $ g ...) とする手はあります.
#「$で書けるように関数を書く」ことにこだわりすぎると、上記のようにSchemeの一般的な引数順と相反するので、あまりそこにこだわるのは得策ではないと思います。それでも、普通に書いていて(foo-foo-foo a b (bar-bar-bar (baz-baz-baz x y z)))のように「式の最後に)))が連なるようなパターン」って結構出てくるもので、そういうのは($ foo-foo-foo a b $ bar-bar-bar $ baz-baz-baz x y z) と書くとすっきりしますよ、というのが「$」の意義かなと。
#多値については今の$のままsplicingを入れるのは話がややこしくなるので、サポートを入れるとしたら英語MLで答えたように多値を期待することを明示する (e.g. ($ list $$ values 1 2) => (1 2) とか) 方向になると思います。これはこれで便利なような気がしています。
##->と->>のように、$にも対になるようなものがあればいいということでしょうか。
#->>と$は制御の流れが逆であること以外は似てますね。で、対応する->にあたるものをどうするかですが、$の場合は例えば ($ take 3 $< f $ g) => (take (f (g)) 3) のように異なる記号を使って展開を変えることはできます。これだと引数の最初にチェインするのと最後にチェインするのを混ぜて使えます。ただ、$, $*, $<, $$などとやみくもに記号を増やしてゆくと覚えることが増えるので、その複雑性の増加と見合うメリットがあるのかどうかという話になるかと。
#私としては記号を増やすよりは、flipのようなものを導入する方が(flipの動作と$の動作が直交しているので)綺麗かなと思います。2引数限定だと不自由なので、むしろ(define (rot f . args) (apply f (last args) (take args (- (length args) 1)))) かな。つまり最後の引数をfの直後に持ってくる。
#対話を通じてGaucheの思想が少しわかってきました。ありがとうございます。
#PostScript の roll みたいですね。逆ポーランド記法だと括弧いらないですけど、書いた次の日には読めなくなってしまいます。
#スタックの状態を脳内でトレースしないとならないPostScriptよりは読みやすいんじゃないかなあと思ったけどHaskellのポイントフリースタイルで (flip f) . g . ... とか出てくると私は頭の中でlambdaを補わないと理解できないから同じようなものかもしれない。
#yamasushiさん、エディタ設定のまとめ@wiliki、とても有用な情報だと思うので、Gauche:EditorSettings みたいなページに独立させてもらうと良いかもしれません。個人名のページだと他の人が編集するのを遠慮してしまうかもしれないので。
#確かに RT: @gengar68 cutみたく ($ take <> 3 $ f $ g) というのはどうだろうと思ってたけど,これだとcutと組み合わせて使えなくて残念だった.っていうか ($ (cut take <> 3) $ f $ g) でよくねって言われそうな気がする.
#gosh> (disasm (lambda () ($ (cut take <> 3) $ f $ g)))
CLOSURE #<closure #f>
=== main_code (name=#f, code=0x1bb1c40, size=13, const=3 stack=5):
args: #f
0 PRE-CALL(1) 8
2 PRE-CALL(0) 6
4 GREF-CALL(0) #<identifier user#g>; (g)
6 PUSH-GREF-CALL(1) #<identifier user#f>; (f (g))
8 PUSH
9 CONSTI-PUSH(3)
10 GREF-TAIL-CALL(2) #<identifier user#take>; (take x 3)
12 RET
#効率的にも、こういうcutの使い方はインライン展開されるので性能を気にする必要がない。
#wilikiのスタイルになっているのかどうかわかりませんが、とりあえず作ってみました。diigoで拾い集めたリンクをコピペしただけですが・・・(汗 http://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3aEditorSettings #上のflipで ($ flip take 3 $ f $ g) するくらいならcutでいいような気もするんですよね.$とcutも直交してますし.逆に記号増やして複雑にする方向も,簡潔に書けるならそれはそれでありだと思います.
#gosh> (define-syntax foo
(syntax-rules ()
((_ (a b ...) ...)
'(((a b) ...) ...))))
#<undef>
gosh> (foo (1 2 3) (4 5 6))
(((1 2) (1 3)) ((4 5) (4 6)))
#Racketだと (((1 2) (4 3)) ((1 5) (4 6))) になるんですが,これはどのように解釈すればいいのでしょうか?
#それはR5RSの範囲では未定義動作だったような気がする。パターンはaには...が1段、bには2段かかってるんだけど、テンプレートではa, bともに...が2段。従ってaについてはパターンより余分に繰り返さないとならない。その繰り返す順番がGaucheとRacketで違ってる。R5RSではパターンとテンプレートの繰り返しのネストが等しい場合しか定義されてなかったはず。
#あっそうか,なるほど.R6RSだとRacketの動作になるんでしょうか.Gaucheの今の動作みたいなのをR6RSでやろうとしたら syntax-rules では書けないのかな…
#ふーむ、R6RSでも"If a pattern variable is followed by more ellipses in the subtemplate than in the associated subpattern, the input form is replicated as necessary." とあるだけでinput formをどういう順番でreplicateするか書いてないような。でもR6RSの議論の時にこの話が出たような気がするんで、明確化されてたと思ったんだけど、どうだったかなあ。
#R5RSでは"Pattern variables that occur in subpatterns followed by one or more instances of the identifier ... are allowed only in subtemplates that are followed by as many instances of ...."で、同じネストレベルしか陽に許してませんね。
#そうですね…"input form is replicated as necessary"ってだけじゃ分かりませんよね.templateの方が数が多い場合に言及しておきながら,実際どう処理するのかちゃんと書いてないってのは変ですね.
#実際にはいろんな繰り返し方をしたい場合がありそうですけど,今の syntax-rules の ... の方式だけだと難しそうですね.
#こんにちは。env HOME=/another/root/dir gosh -ftest test/system.scm が失敗します。discrepancies found. Errors are:
test normalize: expects "/another/root/dir/abc" => got "/root/abc"
#環境は何ですか?
#cygwin でその手の問題はよく起こりますね。
##ああ、これは期待する値の方を$HOMEを使って求めてるせいですね。
#Gauche本体の方ではgetpwuidからホームディレクトリを引っ張ってきてるんだけど、それを照合するための何かを別系統で求めないとならないのか。
#diff --git a/test/system.scm b/test/system.scm
index 9b2c3b2..8056f6b 100644
--- a/test/system.scm
+++ b/test/system.scm
@@ -198,7 +198,7 @@
(gauche.os.windows #t)
(else
(test* "normalize"
- (n (string-append (get-command-output "echo $HOME") "/abc"))
+ (n (string-append (slot-ref (sys-getpwuid (sys-getuid)) 'dir) "/abc"))
(sys-normalize-pathname "~/abc" :expand #t))))
(test* "normalize" (n "/a/b/c/d/e")
#こうしとくか。
#pushした。(d931685)