面白そうですね。yagniって久しぶりに聞きました ;-)
実装を見る感じだと、デッドコードを洗い出すっていうよりは、使われていない関数をリストアップするツールなんですかね
(when false (bar))
みたいなコードは bar を呼び出しているとみなすのかな。ブログ記事に Weaknesses ってあるのでそこみると無理そうな気がしますね。
Clojure ってこういう解析系のツール作るの難しそうというか、役に立たないケースが結構目立つ気がする
Clojureのコードを解析するのに、まず障壁になるのがマクロの存在で、どのシンボルがローカルな束縛を表し、どれがVarの参照になるっていうのはマクロを展開して解析していくcode walkerを書くかtools.analyzerなんかを使う必要があります。
静的解析ツールなんかでさらに問題になるのは、そうやってマクロを一旦展開しきってしまうと、マクロの展開形の中のどのシンボルが展開前のコード中のどのシンボルに対応するかが分からなくなってしまうことですね。
なんか前にもrewrite-cljのときに似た話をしましたけど
(when false (bar))
とかについては無理でしょうね。yagni自体は、コードから関数のコールグラフを作って、他の関数から呼び出されない関数をリストアップするのみで、フロー解析みたいなことには踏み込んでなさそうです。
似た話を僕は前にハレイクでミュータントテストの話をしたときにも聞いたような気がします(笑)
😅
まぁでも、最近Clojureのコードを解析する需要は増えてる印象で、実際今年のGSoCのプロジェクトでもそんなようなテーマで一歩走ってましたね http://dev.clojure.org/display/community/Project+Ideas#ProjectIdeas-Sourcemetadatainformationmodel
興味深いですねー
condのネストが深くなる問題、たぶんこういうコードのことを言っているのだと思うのだけど、
(cond
(= my-val 1)
(do-something1)
(= my-val 2)
(do-something2))
いくつかのオープンソースのライブラリ読むと、
(cond
(= my-val 1)
(do-something1)
(= my-val 2)
(do-something2))
みたいに書いてる例がありました。というか、最初の方の書き方でパッチを送ったら、マージ後に下の方式に書き換えられたことがあります。最初の例の書き方って僕はあまり見たことないですね。
test/expr のペアはだいたいインデントの位置が同じになるのが普通な気がします(それか同じ行
1つめの書き方もどこかでは見たことがありますね
そういう流派が存在するんですね
あと
(cond
(= my-val 1)
,(do-something1)
…)
みたいにカンマをはさむスタイルもあったようなまあ、(if (= my-val 1) とかで次の行でひとつネストするのと同じって考えると上みたいになる。
ネスト深い問題てどこで出たっけって思ったんですが、もしかして reader conditionals の話からきてるんですかねー http://clojurians-log.mantike.pro/clojure-japan/2015-06-18.html
http://clojurians-log.mantike.pro/clojure-japan/2015-06-22.html
いずれにしろ、
(cond [(= my-val 1)
(do-something1)]
[(= my-val 2)
(do-something2)]
…)
より見やすくなってるかというと、個人的には疑わしい気がします。それともこっち?
僕も同じくcondのインデント問題が何なのか把握できてない
ああ、私も > そういえばかなり前に出てた話題ですが、condのインデントが深くなる問題、自分は勝手に cond* という名前のマクロを書いて使ってます。ただこれを他の人もいじるコードでも使うべきかはかなり悩むところ 見て、インデントが深くなるといえばってことで書いた程度なので、もともとどの話題なのかは不明。。。
cond そのものについてならこういう話を昔、 shiro さんが書かれてますね http://practical-scheme.net/wiliki/wiliki.cgi?Lisp%3AS%E5%BC%8F%E3%81%AE%E7%90%86%E7%94%B1 > 個人的には、condは (Paul GrahamがArcで 提案しているように) 括弧をひとつ省いて、(cond 述語 式 述語 式 ...) で いいんじゃないかという気がします。式に複数の処理を書きたければbeginを使うと。
インデント位置を述語と式で揃えると、区切りがよくわからんようになるんで、結局述語のあとに空行いれて区切りを表現しなくちゃいかんのですよね。例えば
(cond
(keyword? value)
(keyword (hyphenate (name value)))
(string? value)
(if (empty? value)
value
(hyphenate (rest value) (lower-case (first value))))
:else
value)))
(cond
(keyword? value)
(keyword (hyphenate (name value)))
(string? value)
(if (empty? value)
value
(hyphenate (rest value) (lower-case (first value))))
:else
value)))
同じ行に述語と式を書かない場合、下が一般的ですかね?
私は前は式をインデントしてましたけど、今は下の書き方をしてます。
個人的には、括弧をなくしたことで
(cond [(short-test) (do-something1)]
[(loooooooooooong-test)
(do-something2)]
…)
みたいな感じに、testの式とthenの式の間に改行を入れたり入れなかったりといった書き方がしにくくなったのが不自由だなぁと思うことが多いですねcaseの場合はさらに、デフォルト節にはtest-constantが書けなくてバランスが悪い問題がありますけど、それはまだ別の話かな?
確かにどっちかに揃えないと読みにくくなりますよね
letに対してはそこまでの不満感がないのは、letの場合は結局ローカルな名前をつけるだけなので(自分でそこまで長い名前をつけなければ)1行が長くなって改行を入れたくなるような状況が少ないことによるのかなぁという自己分析
cond もそうですが extend-protocol するときにも一行空けしますね。こちらはあまり一般的ではないかも。
extend-protocol はたしかに1行空けをよく見ますね
いっそ cond くらいだったら、どうせ test/expr の対なんですから Emacs とかのエディタ支援を受けて test と expr の背景色を変えるとかしてしまってもいいのかもしれませんね(エディタがないと読めないコードとは…