久しぶりにHaskellを触ってみたくなったので、Template Haskellを試してみました。
本当に数年ぶりにstackを使ってみたら・・・ちゃんとビルドしてくれますね!本当にstackには頭が上がりません。
Template Haskellとは
Hashellでメタプログラミングを可能にする仕組みです。
Cで言えばマクロに相当するらしいです。
もう少し具体的に言えば、コンパイル時にコードを生成して埋め込む仕組みです。
大雑把な理解としては・・・
- コンパイラは、コンパイルが開始されたらTemplate Haskellで書かれたところを評価してHaskellコードに変換し、人間が書いたソースコードに埋め込む(これを接合と呼ぶらしい)
- 接合されて完成形となったソースコードをコンパイルする
という流れです。
何が嬉しいのか
Haskellの美点の1つが狭量極まりない型安全性であることを否定する方はおられないでしょう。
Template Haskellは、動的でありながら型安全性を保つ仕組みです。
例えば、SQLのselect文の結果は複数のカラム名から成ります。SQL文を書き直せばカラム名のリストも変化するわけです。
この結果を型安全に取得する方法はいくつかアプローチがあると思いますが、Template Haskellが1つの解決策になります。
1つのアプローチとしてコード生成という手段がありますが、Template Haskellはそれを言語仕様の中に組み込んだものと言えます。
考え方
表記の仕方や準備は置いておいて・・・結局のところ 入力の文字列からHaskellのコードを組み立てるプログラムを書く ことになります。
ということはHaskellのプログラムの構成を知っておく必要があります。
「式」や「定義」など、Haskellプログラム構成要素を押さえていないと、コード生成は難しいでしょう。
ただ、逆に、Template Haskellのライブラリを眺めることで構成要素を知ることが出来るので、調べながら書けば意外と何とかなるものです!
作成した例
実プロジェクトではメッセージを台帳管理して、使う場合はメッセージ番号をキーにして取得する、という仕組みにすることがよくあります。
これを模した仕組みを作ってみました。
手抜きをして、メッセージはファイルではなくてHaskellの中に直接書く形としました。
これが、メッセージを定義してHaskellの関数に置き換えるプログラムです。
[messages|...|]の部分がTemplate Haskellです。
{-# LANGUAGE QuasiQuotes #-}
module Messages where
import Messages.Internal
[messages|
hoge hoge-messages
piyo ピヨ
|]
このmessages
を自作できるのがTemplate Haskellの面白いところです。
挑戦したいこと
なぜTemplate Haskellを扱いたくなったかというと、最近SQLに触れる機会が多いからなのでした。
SQLは集合を扱うとても便利で重要な言語ですが、動的な要素が多く、簡単にミスを作り込んでしまうという側面があります。
Tempalte Haskellのような仕組みを使って、クエリを安全に書いたり、結果を安全に取得できたらいいな・・・と思ってTemplate Haskellを触ってみたのでした。
ただ、世の中にはすごい方々がいるわけです。
例えばVSCodeでSQLを書いていたら、DBからメタデータを持ってきて候補を表示してくれるツールを作っている方とか。
あるいは、C#のLINQ+EntityFrameworkも非常に優れた仕組みですね。あれには脱帽します。(状態の追跡は余計だが・・・)
これらの作品には到底敵わないとは知りつつ、しかも車輪の再発明になるということも重々承知しつつ、やっぱりメタプログラミングって楽しいんですよね!
いつかはTemplate Haskellで安全に長大なSQLを書くツールを作って、Haskellでバックエンドを書いてみたい!と思うのでした。
以上。