(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

Clojure で依存関係の解決に潜むワナ?

お仕事中に盛大にハマって、最終的に一緒にやってる Clojure チョットデキルマンに助けてもらった。

Clojure のライブラリを Leiningen や Boot に含める場合、次のような表記を用いる。

[group-id/name version]

Leiningen や Boot はこれを group-id/name でひとつのライブラリとみなし、もし数あるライブラリの内部で使われているライブラリが重複した場合には、後勝ちで依存性が解決されていく*1

なので例えば foo/piyo というライブラリが a/nyan 0.9, a/nyun 0.8 というライブラリを依存関係に含んでいて、 bar/puyo というライブラリが a/nyan 0.1, a/nyun 1.2 というライブラリを依存関係に含んでいる場合に、 foo/piyo -> bar/puyo という順番で依存性が解決される*2とすると bar/puyo の中で使われているライブラリのバージョンが使われることになる。つまり a/nyan 0.1, a/nyun 1.2 になると思う*3

このとき単純にバージョンが競合しているだけならまだ救いがある*4。 lein deps :tree を使えばどのライブラリからどういう依存があって、競合しているかが一目で分かる。 Boot の場合は boot show -p で競合をどう解決したかが確認出来る(単純な依存性グラフは boot show -d で確認出来ます)。

上述した通り、 Leiningen も Boot も group-id/name というのをひとつのライブラリとみなす。つまり、どういうことかというと、既に気付いた人もいると思うけど、もし「同じライブラリなのに名前が違うとコンフリクトしない」んです。そんなことあるわけがない、って思うかもしれませんが極稀に起こります。例えば ring-anti-forgery 。 ring シリーズのひとつとして group-id に ring をつけるようになったのは 0.3.1 以降です。そして、 0.3.0 以前のものを依存性に持っているライブラリが幾らか存在したりします。そうするとこの問題にあたる可能性が多少あるわけですね。幸運にも依存性の解決の順番が ring-anti-forgery -> ring/ring-anti-forgery だった場合、何も起こりません*5

更に厄介なのが lein ancient のようなツールを使ったとしても、更新を発見することが出来ないため上の例の場合だと ring-anti-forgery 0.3.0 で止まるんですね*6。恐らくライブラリ開発者などで「 lein ancient を使ってるから依存ライブラリは全て最新だ、うぇーい」みたいなことをやっていると、こういう風になってしまうんでしょうね…。

とまぁ、そんなこんなで、この問題だけはツールで解決する方法が恐らくないので、ハマったときの為に頭の片隅にでも入れておくと良いのではないでしょうか*7

*1:多分

*2:この順番がどういう風に実際決まっているのかはよく分からない

*3:多分!!

*4:as usual!!

*5:奇跡!!

*6:ヒュ~

*7:ちなみに僕の場合はなぜか wrap-anti-forgery 関数の引数がひとつしかない(つまり古いはず)のに、依存性で ring/ring-anti-forgery は別に競合していないというつらい感じの状態でハマってました。結局 ring-anti-forgery 時代の 0.2.1 と ring/ring-anti-forgery の 1.0.0 が入っているという状態で面白おかしくハマってました

#渋谷Java 第十一回で Clojure 用の IDEA プラグインである Cursive の紹介してきた。

何をとち狂ったのかセッション枠という 20 分話せる枠をもらってしまったので話してきた。

元々は Clojure 入門みたいな話をしようと思ったんだけど、やっぱり入門以前にエディタとか IDE で「自分たちが普段使っているものが使えるのか」って大事だと思ったので今回は IntelliJ IDEA も使えるよって話をすることにした。
ちなみに何故 Eclipse じゃないのかと言われると、「 Clojure 使いが実際に使っていてシェアが高い」ことも同じくらい大事だと思ったので IDEA にした。あとはたまたま僕が IDEA のライセンスをたまたま持っていた*1からちょうど良いかってのもあった(もし、自分で試してみて納得行くなら Emacs から移ってもいいかなーと思ったしね)。

発表した内容

以下は発表したスライド

Youtube に実際に今日デモでやった内容と同じ内容をアップしているので興味ある人はどうぞ。

結局 Cursive ってどうなの?

スライドの最後に書いてある通りですが、 Emacs 使い的にはまだ「ちょっと微妙」です。ただ、今のところ一番夢見れる IDE なんじゃないですかね。 Light Table や Nightcode は普段から使ってないからなんとも言えないですけど、個人的に Cursive は「かなり、あり」です。あとは IntelliJ IDEA を普段使いしている、もしくは関連 IDE を使っている人なら簡単に使えるはずです。ちょっとマクロ展開ができたりできなかったりするお茶目な不具合とかもわりとヘビーに使った*2ので知ってますが、そのうち直ると思うし期待してます。

たぶん Emacs ユーザー、特に Cider ユーザーにとっては全然機能が足りないというか、ライブラリ開発者とかだとちょっと足りないと思うと思います。 Emacs なら簡単に定義元ジャンプが出来てなおかつ、読み取り専用バッファを書き込み出来るようにして修正して Clojure のコードを評価とか当たり前にやると出来るんですが、 Cursive では定義元ジャンプは出来ても修正して評価が出来ないです。そこが Cursive 使ってて、あーって思ったところですかね。勿論コピーして REPL の中で名前空間変えて評価するっていう方法もあるけど、 Emacs の方が簡単ですね。
それとなんだろう defxxx 系のマクロで定義されている変数/関数は「未定義」扱いされるので、「未定義だよー」ってハイライトされちゃいます*3。この未定義扱いは地味に不便で確かこれは定義元ジャンプ出来なかった気がする。 Paredit 相当の構造的編集機能めっちゃ便利だけど、強制的に括弧を消したり足したり出来ない気がするので、何かの拍子に括弧の対応が崩れるとそれを修正するのがめんどくさい*4

勿論逆に Emacs ユーザーから見た「あ、この機能いいなぁ」というのもちょくちょくあって、 Java のコードに到達したときにソースがなくてもデコンパイルした結果が見れるとか、ソースをダウンロードしてそっちを代わりに表示出来るとかいいなぁと。あと、特別な設定しなくても補完がほぼ完璧に効くのは良かった。デバッグ機能もちらっとしか触ってないので評価しにくいですけど、昔 Java 書いてたときみたいにグラフィカルに触れるのはいいなぁと。

Remote REPL に接続して開発出来るので今のところ Boot 使っている場合はそっちで対応する必要があります*5。まぁ仕事で使ってますが、あまり困ってないですね*6

まとめると Emacs の自由さは最強だと思うんだけど、 IDEA の簡単に色々出来ちゃう便利さはそれはそれでとてもいいので、このふたつを組み合わせた何かがあったらいいのになー*7

反省点

割と IntelliJ とか Clojure とか知っている体で話してしまったのはまずかったかなーと後で反省。あと、ちょっとテンパってデモの中で文字を大きくするの忘れてた。話し方も下手なのでもう少しちゃんと落ち着いて話したい。

余談

スライド中の IDEA のスクリーンショット途中から Macインターフェイスから StumpWM なインターフェイスになってますが、理由はあまりにも Macキーバインドが使いにくかったので諦めて普段の開発環境使ったというだけです。

余談 2

Scala 使ってる人で、「人類には早すぎた」って言ってる人 Clojure やりませんか?と思ったりした。

余談 3

ビズリーチ良いとこだった。

余談 4

次回は Clojure か ClojureScript の話を真面目にしにいきたい。
「ネタじゃない ClojureScript の話」とか。

残念だったこと


今日会えるのを楽しみにしていたので、残念でした。お大事に…。

*1:年に一度 Kotlin を書く時期に必要になる

*2:2 週間くらい開発のお供にしてた

*3:この機能はオフに出来る

*4:一度その機能をオフにして括弧の対応を揃えてから機能をオンにする必要がある

*5: Cursive 開発チームは Boot まだまだ issue 多いので、それ用の対応する気は今のところなく様子見ぽいです

*6:どちらにしても dev 用に watch タスクも一緒に走らせたいので都合がいい

*7:マテ

1 ヶ月間 Clojure を書き続けて感じたこと

まえがき

日本で Clojure を使い始めた 1 人の Clojure プログラマとして、この 1 ヶ月で感じたこととか Clojure をとりまく環境とか書いておきたいなと思う。

日本では流行ってない?

2010 ~ 2011 年頃、空前の Clojure ブームがあったらしい。原因は「 Programming Clojure 」の翻訳本である「プログラミング Clojure」が 2010/01 に出版されたからだと思う。

プログラミングClojure

プログラミングClojure

だいたい、日本語で Clojure の情報を検索しようとすると 2010 ~ 2011 年頃の古い記事が結構ヒットしてくる。残念ながらその頃から継続して Clojure を勉強している人はあまり多くないというのが現状だとは思うのだけど…。さらにに 2011 年は「 7 つの言語 7 つの世界」が発売されたので、その影響で始めた人も多少いるとは思う。

7つの言語 7つの世界

7つの言語 7つの世界

それから 2011 年はこういう話もあったらしい。

ここで、自分に転機が訪れます。2011 年の 9 月 23 日。Erlang Workshop 2011 Tokyo に参加した日です。
エリクソンErlang VM を開発している Patrik Nyblom が ETS に STM を実装したという話しをしている中で Clojure は勉強した方がいいよという話しをしていたのです。正直 STM は何が嬉しいのかあまり良くわかっていません、ただ Clojure はやるべき。という話しを鵜呑みにしてやってみようかなと思ったのです。
なぜ Clojure なのか - Twisted Mind

2011 年に初の Clojure Advent Calendar があったようです。
Clojure Advent Calendar 2011 - PARTAKE

そして、残念ながらそのブームは 2011 年で終わりを告げ、 2012 年は空白の年となっている。

空白の年という表現をしたのは Advent Calendar がなかったのと Tokyo.clj が 2011 年を最後に一度終わりを告げているからだ。更に Qiita の投稿件数を見ると 2012 年は 20 件程しかない*1のであまり使っている人は少なかったであろうことが想像に難くない。

しかし、 2013 年を機に少しずつではあるけどまた波が来ているのかなという印象がある。理由としては「プログラミング Clojure 」の第 2 版と「おいしい Clojure 入門」の出版だろう。それに流れをつけたのは Tokyo.clj の 16 回目が登尾さん(@)の手によって再開されたことと、あと個人的な印象で言えばこの辺でちょっと話題になったりしたからかなという気がしないでもない。確か 2013 年の頭くらいにあった LispMeetUp はキャパギリギリの人数が集まったらしいし、他の Scheme, Common Lisp 回に比べると足を運ぶ人が多かったらしい。ついでにいうと Qiita の投稿数は 59 件と増えてきている。

プログラミングClojure 第2版

プログラミングClojure 第2版

おいしいClojure入門 (Software Design plus)

おいしいClojure入門 (Software Design plus)

あとは「ニャンパスー」で思いがけずバズった、ニャンパス株式会社の件で Clojure という言語の認知度はぐーんと上がったのではないだろうか。

Advent Calendar も復活し全ての枠が埋まっているので、 Lisp 系言語としては快挙だろう。
Clojure Advent Calendar 2013 - Qiita

じゃあ昨年の 2014 年はどうだったかというと、「はじめての Clojure 」が出版されたのと Compojure の開発が再開されたり Om といった React.js のラッパーが流行ったりと Web 系の機運が高まった年だったと思う。

はじめてのClojure (I・O BOOKS)

はじめてのClojure (I・O BOOKS)

Qiita の投稿数は 196 件と順調に伸びている。 Tokyo.clj が順調に開催されたり Clojure 夜会で Kotlin アイドルが LT するなどといった事件が起こっているのもこの年の特徴だと思う。

ちなみに Advent Calendar も全て埋まっている。
Clojure Advent Calendar 2014 - Qiita

2015 年現在はどうかというと #clojure_focus という gen-classcore.async といった Clojure の特定機能にフォーカスした勉強会が好評を博している。 Qiita の投稿数は現時点で 37 とそこそこ調子がいい。また Clojure を使うという会社が求人を出したりClojure を使う会社に Clojure のコントリビューターが転職したりと徐々に仕事で Clojure 使って行くという事例も増えつつある。先日行われた JJUG でも 2 件くらい Clojure の話があったはずです。

全体としてはこの数年で Clojure に流れがきつつあるのは確かだけど、ちょっとまだまだかなというのが正しい認識なのかなと思う。

海外では流行ってる?

個人的な印象だけで言えば間違いなく流行ってきていると思う。 2014,2015 年と Clojure の書籍は数冊出ています。さらに Clojure/conj, Clojure/West とか盛況みたいですし、 ClojureBridge という RailsGirls の Clojure 版みたいなもの*2が行われるなどわりと Clojure の波が海外だと広がってるのかなという印象です。
StackOverflow などの質問も 2 件 /day 以上くらいの頻度で飛んできますし、使おうとしている人はどんどん増えているという印象が大きいですね。また StackOverflow のアンケートでも MOST LOVED の 5 位に食い込んできているので英語圏では人気が高い言語であると言えるかもしれません。

じゃあここまで流れ来ているはずなのに何故日本では流行ってないの?

難しいところですが、英語出来る人と英語出来ない人の間を繋ぐアダプタ的な層になる人が少ないんだという印象を僕は今受けています。結局はプログラミング言語として優れているとか優れていないとかに関わらず、自然言語の壁というのがまず日本人に対しては高いんですよね。僕なんかも毎日必死に英語の技術書読んだりしていますが、日本語だったらもう少し楽に読めるのになぁと常々思っています。
出来れば書籍の翻訳などに携われたらな*3と思っていますが、今のところまだ予定はないです*4Cookbook なんかはいい本なので、早い段階で翻訳されて欲しいです。

Clojure 使い何処にいるの?

僕も知りたいです。ただ、確実なのは間違いなく Clojure 使っている/使おうとしている会社はそこそこ出てきているということです。僕の知ってる範囲だと「株式会社テンクー」「ニャンパス株式会社」「株式会社サイバーエージェント」「株式会社ログバー」「サイボウズ スタートアップス株式会社*5」とか、あと京都の方にあるらしい

また、 Clojure といえば「あの人」というのもなんとなく定着しつつあるのかなと。例えば川島さんとか、えいとすさんとか、登尾さんなど。露出度の高さだとこの三人が露出度高いなーという印象です。

正直 Clojure 界隈ライブラリ/ツールとかどう?

たぶん Emacs じゃないと Clojure 書けないんでしょう?って思ってる方結構多いと思うんですが、最近は IntelliJ のプラグイン*6人気のようです。また LightTable なども開発はちゃんと継続して続いているようですし、 Vim ユーザーでも書けるような環境は整ってきているみたいです*7。なのでもし貴方が Emacs 使いじゃないから Clojure 書けない…と思っているならそれはもう既に誤りで、 IntelliJ ユーザーなんかは簡単にプラグイン追加すれば書けるはずです。

ライブラリについては数は豊富です(そもそも Java のライブラリも使えますが、 Clojure でラップされているのも最近は結構あります)。ただ、バグが結構*8あったりして、僕みたいな初心者が使ってても簡単に踏み抜いてしまうケースがちょこちょこあるので、 PR 出すくらいの気概は必要だと思っています。それからトラブったり、どうやったらいいんだろうというときに日本語の情報がないケースもそこそこある*9ので、 SO に質問飛ばすとか GitHub 上で issue 作るとかはやる必要があったりします。

どうやって Clojure の情報仕入れている?

Planet Clojure と StackOverflow と RedditRSS で購読しています。そんなに流量は多くないので毎日読んでればだいたい読み切れます。

まとめ

ということでなんとなく現在の Clojure をとりまく環境をひと通り説明出来たかなと思います(僕視点ですが)。日本でもやや流行りつつある気配を見せつつも、情報量が少ないのでなんとなく敬遠しているとか初歩の初歩で躓いちゃって 2 歩目が踏み出せないという人も多いと思うので、もう少し裾野を広げていくための動きを取れたらいいなぁと思います。
もっと裾野が広がれば Clojure の書籍も翻訳されやすくなるでしょうし、日本の Clojure 界隈広がっていくと思うので頑張りたいところです。

おしまい。

*1: Qiita というサービスが 2011 年 9 月頃開始なのと 2012 年はあまりまだ Qiita がエンジニア全体に浸透していた感は薄いのであまり参考にはならないが…

*2:余談ですが、 RailsGirls の日本での第 1 回を企画したりした方(not sure)が ClojureBridge に関わっているようです

*3:あるいは自分から 1 冊翻訳しに行くか

*4:ただ僕の出来る範囲でわりと嵌りそうな SO での QA を日本語で説明したりは今のところしていますが

*5:僕のいるところです

*6:Cursive

*7:そもそも Emacs ユーザーは SO のアンケートによると全体の 3.8% くらいしかいないはずで、 Vim ユーザーの方が圧倒的に多いので当然と言えば当然でしょう

*8:極稀に

*9:2011 年よりこっちの情報であまりバグ踏んだとかそういうのあまり見たことないかも

Clojure での素因数分解プログラム

これ読んでたら Clojure で実装したくなったのでやってみた*1

最終的なコードはこれ。

(ns prime-factorization.core
  (:gen-class))

(def prime-numbers
  ((fn f [x]
     (cons x
           (lazy-seq
            (f (first
                (drop-while
                 (fn [n]
                   (some #(zero? (mod n %))
                         (take-while #(<= (* % %) n) prime-numbers)))
                 (iterate inc (inc x))))))))
   2))

(defn prime-factorization [n]
  (if (< n 2)
    [n]
    (loop [dividend n
           prime-factors []
           primes (take-while #(<= % (Math/sqrt n))
                              prime-numbers)]
      (if-let [divisor (first primes)]
        (if (zero? (mod dividend divisor))
          (recur (/ dividend divisor)
                 (conj prime-factors divisor)
                 primes)
          (recur dividend
                 prime-factors
                 (rest primes)))
        (if (= dividend 1)
          prime-factors
          (conj prime-factors dividend))))))

(defn -main [num]
  (println (map str
                (prime-factorization (BigInteger. num)))))

遅延シーケンス使って素数リスト作ってるあたりがかなり大人気ない。この素数リストのコードはココから拝借。
Clojure - 職業プログラマは素数の夢を見るか? - Qiita

元の記事で書かれているような Timer は作らなかった(めんどくさい)。ので、適当にベンチマークだけかけた。
結果。

prime-factorization.core> (use 'criterium.core)
nil
prime-factorization.core> (bench (prime-factorization 8624871152))
WARNING: JVM argument TieredStopAtLevel=1 is active, and may lead to unexpected results as JIT C2 compiler may not be active. See http://www.slideshare.net/CharlesNutter/javaone-2012-jvm-jit-for-dummies.
WARNING: Final GC required 6.637604849087532 % of runtime
Evaluation count : 21840 in 60 samples of 364 calls.
             Execution time mean : 2.980871 ms
    Execution time std-deviation : 194.092642 µs
   Execution time lower quantile : 2.677770 ms ( 2.5%)
   Execution time upper quantile : 3.404867 ms (97.5%)
                   Overhead used : 10.291900 ns

Found 2 outliers in 60 samples (3.3333 %)
	low-severe	 1 (1.6667 %)
	low-mild	 1 (1.6667 %)
 Variance from outliers : 48.4644 % Variance is moderately inflated by outliers

平均的な実行時間約 3ms らしい。速い。

ちなみにマシンスペックこんな感じで、
f:id:ayato0211:20150415124305p:plain
この上に浮いてる VM の中で実行した結果です。

適当に書いたコードはココに置いておきますね。実行可能 jar を置いてあるので java 環境しかなくても動かせます*2

% java -jar target/prime-factorization-0.1.0-SNAPSHOT-standalone.jar 1000
(2 2 2 5 5 5)

余談

最初はエラストテネスの篩をめちゃくちゃ素直に実装したんだけど、元の記事でテストされているような巨大な数を扱おうとすると range とかでリストを作ったタイミングで OutOfMemory とか StackOverflow とか出て死ぬ。まぁデカイからそのタイミングで死ぬことに何の疑問もないけど、遅延シーケンスでどうやって実装したらいいんだろうって悩んだ*3

;;ボツコード
(defn sieve-of-eratosthenes [n]
  (assert (>= n 2))
  (loop [primes []
         numbers (range 2 (inc n))]
    (if-let [m (first numbers)]
      (if (<= m (inc (Math/sqrt n)))
        (recur (conj primes m)
               (filter #((complement zero?) (mod % m))
                       numbers))
        (apply conj primes numbers)))))

なんか僕が Clojure 書くと Clojure ぽくないコードになってしまう…。

理由は再帰を書いてしまっているからだと思う。同じエラストテネスの篩を実装した例と比べるとなんか僕のほうが圧倒的にひどい、つらい。
例えばこれとかすごく読みやすい。

(defn sieve [[xs ps]]
  (let [[p & more] xs]
    [(remove #(zero? (rem % p)) xs) (cons p ps)]))
 
(defn primes [n]
  (if (< n 2)
    []
    (->> [(range 2 (inc n)) nil]
         (iterate sieve)
         (drop-while #(< (ffirst %) (Math/sqrt n)))
         first
         (apply concat))))

無限遅延シーケンスによる解法は優雅で,しかも速い.しかし,遅い.とはいえ結局何とかなる. - tnoda-clojure

がんばろうって思った。

追記

少しだけ書き直したら読みやすくなった。

(defn- iter [[dividend prime-factors [divisor & rest-primes :as primes]]]
  (let [divisor (or divisor dividend)]
    (if (zero? (mod dividend divisor))
      [(/ dividend divisor) (conj prime-factors divisor) primes]
      [dividend prime-factors rest-primes])))

(defn prime-factorization' [n]
  (if (< n 2)
    [n]
    (let [primes (take-while #(<= % (Math/sqrt n)) prime-numbers)]
      (->> [n [] primes]
           (iterate iter)
           (drop-while #(not= (first %) 1))
           first
           second))))

ただ同じ条件でベンチマークしたら、コードが綺麗な方が遅くなった。読みやすさと速さを両立するのは難しい…。

*1:多分昔、 Scheme かなんかで同じようなの実装した気がするけど、、、

*2:誰が動かすんだ感

*3:結局、他人のコード借りてるわけだけど

もらったもの

これで「なんかください」って書いてたらおふたりから頂いてしまった。ありがとうございますm(__)m

f:id:ayato0211:20150404160436j:plain

@さんから

Clojure Cookbook: Recipes for Functional Programming

Clojure Cookbook: Recipes for Functional Programming

@さんから

Clojure Programming

Clojure Programming

  • 作者: Chas Emerick,Brian Carper,Christophe Grand
  • 出版社/メーカー: Oreilly & Associates Inc
  • 発売日: 2012/04/19
  • メディア: ペーパーバック
  • クリック: 26回
  • この商品を含むブログを見る
Mastering Clojure Macros: Write Cleaner, Faster, Smarter Code

Mastering Clojure Macros: Write Cleaner, Faster, Smarter Code

Web Development With Clojure: Build Bulletproof Web Apps With Less Code

Web Development With Clojure: Build Bulletproof Web Apps With Less Code

頑張って Clojure 勉強するぞい(つらいのは全部洋書なんですよね)。