読者です 読者をやめる 読者になる 読者になる

八発白中

はてなブログに引越しました。

Day 10: SxQL

これは fukamachi products advent calendar 2016 の10日目の記事です。

今日はSxQLについて話します。

Common LispのWeb開発に足りないもの

Cavemanをリリースしてから継続的な課題として考えていたのは、Common LispRDBMSとの連携が貧弱なことでした。CL-DBI (7日目参照) はできましたが、未だにSQLは文字列で書いており抽象度が足りません。

O/Rマッパーに相当するものが最終的に欲しいのですが、一足飛びには行きません。そこでまず何が必要かというと、やはりSQLを抽象化してCommon Lispで扱いやすくすることでしょう。文字列ではなくS式で書けるSQLビルダーが理想です。

SxQL

SQLのS式表現としては以前からs-sqlというものがありました。ただしこれはPostmodernに付属しており、名前に反してPostgreSQLで使うことを意図しています。

使えないこともないのですがPostmodernにバンドルされているというのも気に入りません。新しいO/Rマッパーを作ったとしても、依存ライブラリがPostmodernなんてマヌケな話になりかねません。

そこで例によって新しく作ることにしました。それが「SxQL」です。

Optimaと設計

当時、友人の@m2ymさんがOptimaというCommon Lispのパターンマッチングライブラリを公開して話題になっていました。

実を言うとSxQLの実装はOptimaの実装を真似しています。Optimaは各パターンを構造体で定義しており、提供するAPIはマクロでラップしています。こういう設計にするとインターフェイス部分のマクロが無駄に複雑にならないですし、裏側を知っている人はパターンが自由にプログラム可能という利点があります。

(select :* (from :user) (order-by :created_at))
;=> #<SXQL-STATEMENT: SELECT * FROM user ORDER BY created_at>

;; 単純な関数呼び出しに展開される
(macroexpand-1 '(select :* (from :user) (order-by :created_at)))
;=> (MAKE-STATEMENT :SELECT (FIELDS :*) (FROM :USER) (ORDER-BY :CREATED_AT))
;   T

また、SxQLはSQLをclauseやstatementなどに分離し、それを組み合わせることで最終的に文字列を作り出すという仕組みになっています。

たとえばSELECTFROM部分だけを作ることもできます。

(from :user)
;=> #<SXQL-CLAUSE: FROM user>

これによりWHERE部分だけを作っておいていくつかのSELECT文で使いまわしたりすることも可能になっています。

おわりに

SxQLはGitHubで公開されており、現在Starは81です。

明日のアドベントカレンダーは11日目のIntegralについてです。お楽しみに。