今年もアドベントカレンダーの季節がやってきました。この1年のClojure/ClojureScript界隈の動きを振り返ってみましょう。
Clojure 1.10
11/27にRC2がリリースされ、まもなくClojure 1.10のfinalリリースになりそうな見込みです。
今回のリリースではエラーメッセージの改善やpreplを中心として、開発環境をよりよくするための種々の仕組みが入りました。以下に1.10における主要な変更についてざっとまとめます。
先日開催された勉強会用にまとめた発表資料があるので、個々の変更の詳細についてはそちらなり一次情報である公式のchangelogにあたるのがいいでしょう。
エラーメッセージの改善
1.10では、長年分かりにくいと言われ続けているClojureのエラーメッセージにてこ入れがなされました。
まず、エラーが発生するタイミング(フェーズ)をリード時、マクロ展開時、コンパイル時、実行時といったように分類し、エラーのフェーズごとにユーザに提示すべき情報が整理されました。また、1.9でclojure.specが導入されたことにより却って煩雑になっていたスペックエラーがコンパクトに表示されるような変更も入っています。
datafy
clojure.datafy
という新しい名前空間が追加され、Datafiable
と Navigable
という2つのプロトコルが加えられました。
(defprotocol Datafiable
:extend-via-metadata true
(datafy [o] “return a representation of o as data”))
(defprotocol Navigable
:extend-via-metadata true
(nav [coll k v] "return v in the context of coll and k (a key/index or nil)"))
これらのプロトコルを実装した型について、「データ化(Clojureのマップやベクタなどのデータ型による表現への変換)」をすることができるようになります。詳細について公式の説明はまだありませんが、データのインスペクション等の用途で使うことが想定されているようです。これについては、後述する今年のClojure/conjの発表でも少し触れられています。
メタデータによるプロトコルの拡張
また、datafyが導入される過程で、メタデータによるプロトコルの拡張という新機能が取り入れられました。
これまでプロトコルを実装する方法は、レコードやデータ型による直接実装かextend-protocol
/extend-type
による拡張だけでした:
(defprotocol P (m [this]))
;; レコードによる直接実装
(defrecord R []
P
(m [this] 42))
(m (->R)) ;=> 42
;; extend-protocol/extend-typeによる拡張
(extend-protocol P
Long
(m [this] this))
(m 101) ;=> 101
1.10以降では、メタデータの中にプロトコルの実装を与えることで、プロトコルメソッドを呼び出したときにその実装が使われるようになります。これによって、たとえば個々のマップごとに異なる振る舞いを定義することができるようになりました:
(defprotocol P
:extend-via-metadata true
(m [this]))
;; マップ {:x 42} に独自の振る舞いを持たせる
(def obj
(with-meta {:x 42}
{`m (fn [this] (:x this))}))
(m obj) ;=> 42
;; さらにメタデータを上書きすることで振る舞いを書き換えることも可能
(m (with-meta obj
{`m (fn [this] 43)}))
;=> 43
ただし、メタデータによる拡張はデフォルトでは無効になっていて、有効にするにはプロトコル定義時に :extend-via-metadata true
を指定しておく必要があります。
pREPL
1.10では、pREPL (“programmable” REPL) という新しいストリームベースのREPLも追加になりました。これは、1.9で導入されたソケットサーバからうまく利用できるREPLとして取り入れられたものです。
これまで、開発環境等の別プロセスからClojureへ接続する場合のプロトコルとしてはnREPLが使われることが多かったのですが、pREPLはよりシンプルなプロトコルではあるが拡張のしやすさが特徴になっています。
Clojure CLIからpREPLサーバを立ち上げるには、以下のようなオプションをつけてClojureを起動します:
$ clj -J-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/io-prepl}"
Clojure/conj 2018
Clojureの最も大きなカンファレンスであるClojure/conjは、今年はちょうど日本時間の11/29夜から始まりました。まだ会期中ですが、すでに発表の動画はYouTubeにアップされ始めています。
そのなかで、Stuart HallowayのREBLの話とRich Hickeyの “Maybe Not” はインパクトが大きそうな発表だったのでここで紹介しておきます。
REBL
REBL (Read-Eval-Browse-Loop) はインスペクタのついたREPLといったようなもので、Clojureの開発コアチームが所属するCognitect社が開発しているツールです。REPLで評価した結果のデータをインスペクタで潜って確認できる機能を持っています。
内容的には、pREPLとdatafyで実現できる開発環境についての説明という位置づけで、1.10のこれらの機能が今後Clojureの開発環境をどのように変えていくポテンシャルがあるかを示す発表ではないかと思います。
REBLのソースコードは今のところ公開されていませんが、ツール自体は無償でダウンロードして使えるようになっています。
Maybe Not
Rich Hickeyによるオプショナルな値の表現についての発表でした。
静的型のある言語では、型システムによっては Maybe a
な型を a
に変えたりその逆をしたりすることが破壊的変更になってしまうのがよくないという批判が発表中ではなされていました。と同時に、clojure.specが持つ s/nilable
や s/keys
も設計としてまずい部分があると認めています。そして、今後これらのスペックは新しい s/schema
と s/select
によって置き換えることが検討されているとのことです。
Clojurists Together
昨年立ち上がったClojureで書かれたOSSの開発を支援する活動であるClojurists Togetherは、1年4期間の間に8つのClojureプロジェクトを支援してきました。
各期に支援を受けたプロジェクトは次のとおりです。各プロジェクトの詳細については以下のページから確認することができます。
2018年第1期
2018年第2期
- CIDER: Emacs上に高機能な開発環境を実現するマイナーモード
- ClojureScript
2018年第3期
- cljdoc: Clojure/ClojureScriptのドキュメンテーションサイト
- Shadow CLJS: 軽量なClojureScriptビルドツール
2018年第4期
- Kaocha: 高機能なテスティングライブラリ
- DataScript: Datalogを使ったインメモリデータのクエリエンジン
軽量な開発ツールの台頭
1.9でClojure公式のCLIコマンドが導入されたことにより、それまで主流だったLeiningenやBootといった高機能なビルドツールを使わずに開発をする軽量な開発方法が徐々に広がりつつあります。
これまでLeiningen等に組み込まれていたテストランナーやパッケージングの機能等は単一のライブラリとして切り出して提供する動きが進んでいます。Clojure CLIから使われることを想定したツールは以下のページにまとめられています:
また、tools.deps
(Clojure CLIのコア機能を提供するライブラリ)がgitリポジトリを依存ライブラリとして指定できるようになり、Clojarsやその他のMavenリポジトリに公開されていないけれどGitHub上では公開されているClojureプログラムを依存ライブラリとして扱えるようになりました。
この機能は使用するライブラリをコミット単位で指定できるようになっているため、Clojars等で配布されているライブラリであってもまだリリースはされていない機能を利用したり、特定のコミット時点でのプログラムの挙動を確認したりといった、より細かい単位でのコードの共有を可能にしました。
さらに副次的な効果として、GitHubのgistはgitリポジトリとしての側面もあるため、gist上に掲載したコードをCLIコマンドから直接でき、ちょっとしたサンプルコードを手軽に共有し、実行する手段として一部で利用されています。gistから直接実行可能なコードとしては、たとえば以下のようなものがあります:
Datomic Ions
tools.deps
の別の利用例として Datomic Ions も登場しました。
Datomic Ionsは内部的にDatomic Cloudを使ってアプリケーションコードを管理しつつ、Clojureで書いたAWS Lambdaアプリケーションのデプロイを簡単にしてくれるツールおよびサービスです。一旦初期設定さえ済ませてしまえば、ClojureのコードとEDNの設定ファイル、コマンド群で完結し、Lambda関数も自動的に作ってくれるのでお手軽です。
- https://docs.datomic.com/cloud/ions/ions.html
- https://docs.datomic.com/cloud/ions/ions-tutorial.html
GraalVMでのビルド
Clojureの最近の使い方で注目を集めているものとしてGraalVMでのビルドがあります。
GraalVMはざっくりとはJVM言語を含めた多言語対応のランタイムですが、native-image
というネイティブバイナリへのAOTコンパイル機能も持っています。この機能を使って、まずClojureをコードをAOTコンパイルし、生成されたクラスファイルを native-image
に渡すことで、結果的にClojureで書かれたプログラムをネイティブコンパイルすることができます。
これにより、従来は起動に時間がかかるためClojureには不向きとされてきたコマンドラインツール開発等の分野も、Clojureの現実的な適用領域として視野に入るようになってきました。実際、GraalVMでビルドしたClojureプログラムが瞬間的に起動するようになったという報告が上がるようになってきています。また、小さなコマンドラインツールだけでなく、Webアプリのような実用的な規模のアプリケーションもビルドできる例があるようです。
- https://www.astrecipes.net/blog/2018/07/20/cmd-line-apps-with-clojure-and-graalvm/
- https://www.innoq.com/en/blog/native-clojure-and-graalvm/
ただし、Graalの制約上実行時にクラスロードするようなコードは実行時エラーになるとのことで、Clojureでは特にevalを含むようなコード(eg. REPL)は正常に動作しないようなので注意が必要です。
native-image
を使ったClojureコードのビルドを簡易化するツールもすでに現れています。
ClojureScriptの進展
Clojurists Togetherで開発が促進されたClojureScriptにも大小さまざまな改善が入りました。
特に際立った進展としては、以下が挙げられるでしょうか。
- cljs.main
- pREPLの実装
- GraalVMとの連携
cljs.main
Clojure CLIから呼び出して使えるエントリポイントとして cljs.main
が追加されました。これにより、
$ clj -m cljs.main
とするだけでClojureScriptのブラウザREPLが起動できるようになりました。ランタイムとしてNode.jsを使う場合はREPL環境に node
を指定して
$ clj -m cljs.main -re node
とすると起動できます。
これまでClojureScriptはビルドのセットアップの煩雑さから入門のハードルが高い難点がありました。cljs.main
の追加により、コマンドラインから簡単にClojureScriptのREPLを起動できるようになったことでこのハードルが大きく引き下げられました。
pREPLの実装
1.10.238からClojureScriptのREPLをpREPL経由で起動できるようになりました。Node.jsをランタイムとしてClojureScriptのpREPLを起動する場合、Clojure CLIを使うと以下のように起動できます。
$ clj -J-Dclojure.server.repl="{:port 5555 :accept
cljs.server.node/prepl}"
Clojure/ClojureScriptがともにpreplでやりとりできるようになることで、今後便利に使えるpREPLクライアントが登場してくると、それをClojure/ClojureScriptのどちらのREPLでも使い回せるような利点が出てきます。
GraalJSのサポート
ClojureScriptのサポートするランタイムとしてはこれまでブラウザ、Node.js、Nashorn、Rhinoがありましたが、1.10.439からはそれらに加えて新たにGraalVM (GraalJS)がサポートされるようになりました。Nashornは長期的には廃止されていく見込みのため、Nashornの担っていた役割を代替する形でGraalJSがサポートされることになりました。
GraalJSをランタイムとしてClojureScriptを起動する場合は、REPL環境に graaljs
を指定します:
$ clj -m cljs.main -re graaljs
まとめ
振り返ってみると、サーバレスアプリケーションを作りやすくするツールが現れたり、Clojureをネイティブコンパイルしてコマンドラインツールの用途でも使いやすくできるようになってきたりと、Clojure/ClojureScriptの用途がますます広く、そしてより便利になりつつあるというのが感じられる1年だったかなと思います。
そして、1.10リリース後にはclojure.specの次期版である spec2 の開発が進められる予定です。こちらもどう発展していくのか注目です。