紙箱

覚えたことをため込んでいく

いまどきのClojureのはじめかた

Clojure 1.9あたりから、Clojureの始め方が大きく変わったのですが、その辺りをまとめた記事が見当たらず、すでにClojureをやってる人しか知らない状態っぽいので、急ぎで書いてみました。

大きく変わったのは、 clojure および clj というコマンドが導入されたことです。これまではClojureの実行には Leiningen のようなビルドツールを使うのが一般的で、スクリプト的なコードを書くのには向いてない印象でしたが、1.9からは、 clojure コマンドに .clj ファイルを渡すと実行できるようになりました。また、コマンドが用意されたことで、シェル・スクリプト冒頭に #!clojure コマンドへのパスを書くことで、シェルスクリプトとしてClojureコードを記述できるようになりました。

この二つのコマンドをインストールする手順が、環境ごとに用意されています。

インストール

Mac

homebrewに対応してます。

brew install clojure

Linux

まだapt-getやyumとかには対応していませんが、インストール・スクリプトが用意されているので、それを実行すれば簡単にインストールできます。

こちらのページに書いてある3行のコマンドを実行すると、Clojureがインストールされます。

https://clojure.org/guides/getting_started#_installation_on_linux

Windows

残念ながら、まだ提供できていないようです。 とはいえ、clojureは単なるjarファイルなので、clojure.jarを手動配置することでなら、もちろん実行可能です。

しかし、Microsoft自身がWindowsで動くLinux環境を提供しているので、そっちで実行した方が簡単なように思います。

REPL

なにはともあれ、REPLです。 Clojureをインストールすると、cljコマンドとclojureコマンドの2つのコマンドが使えるようになります。

cljコマンドのほうがREPL実行用コマンドです。単にclojure REPLの実行だけではなく、REPL上でのコマンドヒストリ機能などがオンになるよう、セットアップしてくれます。

clojureコマンドは.cljファイルを実行するためのコマンドです。こちらがあるので、clojureがインストールされている環境であれば、 #!/usr/bin/env clojureスクリプトファイルの先頭行に書くことで、シェルスクリプトのようにclojureプログラムを実行することもできます。

というわけで、cljコマンドを実行すると、Clojure REPLが起動します。

(+ 1 2)
;;=> 3

(require '[clojure.string :refer [split upper-case]])
;=> nil
(->> (split "aaa,bbb,ccc" #",")
    (map upper-case))
;=> ("AAA" "BBB" "CCC")

Clojureの関数を覚えてくると、ちょっとした計算とかはREPLでささっとプログラム書いたりしてしまいます。

使い終わったら、Ctrl+dで終了です。

スクリプトを書く

Clojure 1.9からは、Clojure自体に依存ライブラリを処理する機能が組み込まれました。1.8までは、Leiningenなどのビルドツールが必要でしたが、必要なことが「依存ライブラリの自動ダウンロード」だけなのなら、Clojure単体でできるようになったのです。

もちろん、ビルドツールには依存性管理以外にもいろんな機能があります。そちらを使いたい場合(「プロジェクト」規模になると大抵は必要でしょう)は、Clojure単体では難しいので、すなおにビルドツールを使いましょう。一番メジャーなのは Leiningen です。こちらもインストールスクリプトがあります(Windows用のbatファイルもあり)

さて、ちょっとしたスクリプトを書きたいけど、そのためには外部ライブラリが必要だ、というケースはよくあります。たとえばClojureでhttpアクセスが必要な場合、clj-http という有名なライブラリを使うことが多いですが、当然これは、別途用意する必要がありました。

Clojure 1.9には、 deps.edn という特殊なファイルをカレント・ディレクトリに配置することで、必要な依存ライブラリを自動ダウンロードする機能が追加されました。

clojureコマンドを実行するときに、カレント・ディレクトリにdeps.ednがあれば、その中に記述された依存ライブラリをすべてダウンロードしてから、スクリプトを実行します。つまり、スクリプトとして書いたclojureプログラム(テキストファイル)といっしょにdeps.ednを配ることで、スクリプトから外部ライブラリを利用することができます。

deps.ednの解釈は、clojureコマンド実行時だけでなく、cljコマンド実行時にも行われますので、REPLで作業したいけど、その作業には外部ライブラリが必要、というときにも、ささっとdeps.ednを書けば、REPL内で外部ライブラリを使うことができます。

deps.ednは次のような、Clojureのマップ文法で書かれたファイルです。

{:deps
 {clj-http {:mvn/version "3.9.0"}}}

このようなファイルがおいてあるディレクトリでcljコマンドを実行してみましょう。REPL起動時にclj-httpのダウンロードが行われます。

ちょっと、http接続を試してみましょうか。

(require '[clj-http.client :as http])
;=> nil
(http/get "https://google.co.jp/" {})
;=> GoogleページのHTMLテキスト

こんな感じで、必要なライブラリを使ってREPLで作業できるわけです。

deps.ednを使ってもう少し複雑なプログラムを組む

1ファイルというほど小さくはないけど、ビルドツール使うほどでもない、使い捨てのツールプログラムを作る、みたいなこともあるでしょう。Clojureだと、プログラムは名前空間に分割して書いていくのが普通で、そうするとファイルも複数になりますし、名前空間に階層がある場合は、ディレクトリも必要です。

deps.ednと同じ場所に src というディレクトリがあれば、その下にソースファイルが配置されているものと解釈します。srcの下に、名前空間に合わせたディレクトリ階層を作れば、ちゃんとソースを見つけ出してくれます。

- deps.edn
- helloworld.clj
- src/
 - util/
   - string.clj
   - net.clj

上記のように配置すれば、このプログラムは、 helloworld.clj という実行用スクリプトとは別に、

  • util.string
  • util.net

の2つの名前空間が追加されたプログラムとなるわけです。たとえば、 helloworld.clj から、requireを使って、util.stringutil.net名前空間を利用することができます。

Clojure単体でも、deps.ednと組み合わせることで、ちょっと規模の大きめのプログラムでも、作ることができるわけです。

エディタを用意する

ClojureLISP系の言語ですので括弧を多用しますが、世のLISPerたちがどう括弧を扱ってるかというと、括弧の対を「見づらいもの」としてではなく、むしろ利用すべき「構造」と捉えています。全てが括弧で囲われているのだから、括弧単位で移動したりカットしたりできる、専用のエディタ機能を使っています。この操作に慣れると、括弧にはむしろありがたみを感じるのです。

Clojureプログラマは、エディタの力を使って、括弧を巧みに利用します。ただのテキストエディタで括弧を扱うのは、誰でもつらいものです。それは、他の言語でも同じです。おなじみの言語を、何のサポートもないテキストエディタで書こうとすると、結構大変なはずです。

また、書いているプログラムをREPLを使ってさっと動作確認しつつ書いていく、というスタイルがメジャーなのもあり、エディタから簡単にREPLを起動したり、REPLにソースを送り込んだり、REPLから作成中のプログラムの名前空間にアクセスして関数を実行したり、プロジェクト内の関数を探したりしたい。いわば、IDE的な機能が欲しいです。

これらのサポートなしにClojureコードを書くのはやはり大変で、ただのテキストエディタJavaを書く的なしんどさがあります。だから、Clojureをサポートしたまともなエディタを用意しましょう。

Clojureでもっともメジャーな開発環境は

Emacs + CIDER

です。エディタとしてemacsを、そのプラグインのCIDERをIDE機能として開発します。

もし、Emacsは得意じゃないけどVimは使える、という方々は

Spacemacs + CIDER

です。SpacemacsはVimキーバインドで使えるemacsのようなもので、CIDERも使えます。

しかし、どちらも心得がない、という方が、プログラム言語をはじめるのにEmacsVimのような複雑なエディタを覚えるところから始めないといけない、というのはあまりにもハードルが高いと思います。

なので、私の一押しは、最近いろんな言語をサポートしてることでユーザーの多い、IntelliJ IDEAを使うことです。IDEAのプラグインとして Cursive というClojure開発支援プラグインがあります。こちらはビジネスで使う場合は有料ですが、オープンソース開発や学習用とでは無料です。有料版は、個人に紐付くライセンスだと99ドルです。企業契約で個人に紐付かせない(いわゆる「ライセンス数」で買うタイプ)だともうちょっと高いです。

とりあえず、学習用に試す分には無料ですのでおすすめします。基本的なエディタ操作は、IDEAの標準操作がそのまま使えますので、すでにIDEA使っているなら学習コストも低いです。CursiveにはREPLサポートもあるし、作業中の関数をREPLに送る機能なども備わってます。

(ビジネスでつかうなら、自分が仕事で使うツールを作ってくれた作者への敬意として、ちゃんとお金を払おう)

構造編集 (Structural Editing)

いずれのエディタを使うにしても、括弧をかっこよく効率的に扱うための 構造編集(Structural Editing) を覚えることをおすすめします。PareditとかParinferが有名です。Spacemacsだと、SPC+kを押したら表示されるメニューに、構造編集のための項目があります。

Clojureでコードを書いていると、「この括弧内に次の行の括弧を丸ごと移動したい」とか「この部分を括弧の外に追い出したい」とか「この括弧内のコード、もう不要だから丸ごと消したい」とか「この括弧全体を丸ごとコピーしたい」といったことがしょっちゅうあります。構造編集でこれができます。

(my-great-func {:name "yano", :place "japan"})

のようなプログラムがあるとして、「おっと、この my-great-func を実行するには条件があるんだった。whenかifで囲わないと...」と考えたとします。

(when condition (my-great-func {:name "yano", :place "japan"})

とか書いてバグるわけです(上のコードは、閉じ括弧が足りません)。

まず、構造編集使用中は、括弧は常にペアで書き込まれます。開き括弧を書くと閉じ括弧も自動的に打ち込まれます。次のような感じになるでしょう。

(when condition)
(my-great-func {:name "yano", :place "japan"})

勝手にペアになるので、(when conditionと書いていくと、必ず括弧の内側になります。

ここで、conditionの後ろに、(my-great-func ...)の括弧を丸ごと持ってきたいわけです。

キー操作はエディタによって異なると思いますが、Spacemacs+CIDERなら、when condition の括弧の内側にカーソルがある状態で、「SPC k s」とキーを打つと、「Slurp(吸い取る)」という操作が実行されます。つまり、(when ...) の次にある(my-great-func ...)という括弧のブロックを、(when ...)の括弧の中に吸い込むわけです。

結果として、次のようなコードに変わります

(when condition
   (my-great-func {:name "yano", :place "japan"}))
  • 括弧を打ち込むと常にペアで打ち込まれる
  • 括弧単位で、隣の括弧を吸い込んだり、今の括弧を外に追い出したりすることで、カット&ペーストによって間違って括弧を消してしまうなどのミスが起きなくなる
  • 括弧単位で移動するので単なるカット&ペーストより圧倒的にミスりにくい

Clojureプログラマ(そしておそらくはLISPプログラマ)は構造編集で括弧を扱っているので、括弧を「どこで閉じてるのかよくわからない読みにくいもの」ではなく「括弧単位で追い出したり吸い込んだり、カットしたりペーストしたりできる、便利な編集単位だ」と思ってるのです。かならず開き括弧と閉じ括弧で囲われている、というLISPの特性があってこそなのです。

余談ですが、Clojureは括弧の種類が () だけではなく、[], {}, なども使うので、エディタ上ではこれらが色分けされてちょっと読みやすい、という利点もあったりします。

構造編集にはいろんな機能があって、正直私も全部覚えてはいないです。

  • Slurp (隣の括弧を吸い込む)
  • Barf (括弧を追い出す)
  • カット (括弧単位でのカット)
  • コピー (括弧単位でのコピー)
  • 削除 (括弧を丸ごと消す)

この5つを覚えるだけで、作業効率が全く変わってくるので、おすすめです。

SlurpとBarfは、「前を吸い込む」と「後ろを吸い込む」など、前後どっちを処理するかで、それぞれ2種類ありますが、まずは「後ろを吸い込む」「後ろに追い出す」のほうを覚えることをおすすめします。実際のコーディングでやりたいことは、大抵こっちのはずです。慣れたら前も処理できるように覚えればいいでしょう。

構造編集は、Emacs, Spacemacs, Cursiveのいずれのエディタでも使えます。ただし、これらの機能を呼び出すキー操作はエディタによって異なっているので、自分の好きなエディタでの操作を調べてみてください。

まとめ

JavaVMが入っている前提であれば、Clojure 1.9からは、 clojureコマンドのインストール だけで、簡単にClojureを始められ、シェル・スクリプトのようにClojureコードを実行することができるようになりました。若干ハードルが下がったように思います。また、 deps.edn が導入されたことで、外部ライブラリをスクリプトコードから簡単に利用することできるようになりました。

ちょっと、試してみるのはどうでしょうか。