Scheme用Linterの改良(マクロ逆展開を可能なかぎりやる?)Niyarin
View Slide
Scheme用Linter、Red-parenについて○パターン列挙型のLinterRed-paren(apply append(map some-proc (foo x)))ソースコード((apply append (map f ls …))(append-map f ls …))… 略)パターン(rules+)(append-map some-proc (foo x))と書けるよniyarin/red-paren
Red-parenのちょっとした問題○マクロに対して弱い例) threading macro (ネストした式を平らにする)(->> some-list(filter (λ (x) (not (some-pred? x))) )… )(->> some-list(remove some-pred?)…)と動いてほしい(filter (lambda (x) (not (pred x))) ls)このマクロを使うとremoveのルールにマッチしないremoveのルール
マクロが問題になるケースは多いのか?普通のマクロは問題ない○ 問題の原因の多くは手続き適用を壊すマクロ・ threading macro・ condp○レアケースと思って良さそう(たぶん)※これらが悪だとは言っていない(私はcondpが好きです)
マクロ展開→ Lint適用(->> some-list(filter (λ (x) (not (some-pred? x))))なにか1なにか2なにか3)(なにか3 (なにか2 (なにか1 (remove some-pred some-list))))マクロ展開+Lint○ マクロを使うとすり抜けるのでマクロ展開してからLintを書けてみる☓ これは求めていたものとは違う(ちょっと不親切)
マクロを逆展開する機構が欲しい(->> some-list(remove some-pred)なにか1なにか2なにか3)(なにか3 (なにか2 (なにか1 (remove some-pred some-list))))○ マクロを逆展開する操作があれば理想的な指摘コードが得られるマクロ逆展開前ページの例の続き
syntax-rulesって逆にしても使え”そう”Schemeの標準(R7RS)のマクロシステム○ let → lambdaの例lambda → let も逆からマッチさせることでできそう(syntax-rules ()((let ((name val) ...) body1 body2 ...)((lambda (name ...) body1 body2 ...) val ...)))
syntax-rulesって逆にしても使え”そう”そう簡単にはいかない(1) ruleに同じ変数を含んではいけない→ これは同じ変数が同じものにマッチしているならよいことにする(2) 2回以上のellipsis→ backtrack+最短マッチで妥協する?(3) ruleだけに存在する変数や_→ あきらめる(ほとんどないケースだと思う)(4) ruleは上からマッチする仕様だが、逆は必ずしもそうではない→ 次のページ(5) hygenic性とか先頭がsymbolでないことも目をつぶる
復元できそうなものを全列挙する(->> (a b) c)(->> (->> (a b) c)) (->> b a c) (->> b (a) c)決定的に出せないので全列挙→ どれか1つが理想的なもの
逆展開例(->> なにか1なにか2 (なにか3 なにか4) (filter (lambda (x) (not (some-pred? x)))) なにか5)(->> なにか1 なにか2 (なにか3 なにか4) (remove some-pred?) なにか5)(->> なにか5 (->> (remove some-pred? (なにか3 なにか4 (なにか2 なにか1)))))(->> なにか1 (なにか2) (なにか3 なにか4) (remove some-pred?) なにか5)理想不自然なコード(正しく動作はする)理想と近いが正しく動作しない元のコード
どのように理想的なコードを見つけるのか適当な評価式を与えて最もポイントが高いのを選択する○ (同じ要素が同じ順序で含む数 , - 最大の深さ , - 式の長さ )・一応、threadingマクロ例の理想コードはこれでとれはする理想:(5 -2 -6)不自然なコード:(1 -5 -3)動作しないコード:(4 -2 -6)・てきとうなので改良はいる
sexp-diffとかもよいかもしれない・Racket系のs式diff#:newと#:oldを付けて差分が分かる> (sexp-diff'(->> some1 some2 (some3 some4) (filter (lambda (x) (not (some-pred? x)))) some5)'(->> some1 some2 (some3 some4) (remove some-pred?) some5))'((->>some1some2(some3 some4)#:new(remove some-pred?)#:old(filter (lambda (x) (not (some-pred? x))))some5))
Red-parenに組み込む・マクロ情報をユーザが渡さないと使えない→ 名前空間からマクロを得る部分が必要というわけで組み込むのはまだ先・マクロ逆展開は実装済み・信頼性は落ちるのでなんらかのタグをつける
おわり