yunomuのブログ

酒とゲームと上から目線

Template Haskellで遊んでみる

メタプログラミングって最近よく聞くけど、必要性をあまり感じてなくて放置していたんだけど、よくよく調べるとCでいうところの関数マクロでコード書いたり、ソースを解析してライブラリのヘッダファイルを生成したりとか、普通にやってる事をもう少し言語の機能を使ってまじめにやりましたという感じだったのでちょっとやる気が出た。
プログラマならプログラミングが面倒くさいと思うのは当然の思考でしたという感じ。

ということで、TemplateHaskellです。
実のところ暇だったのでこの記事を読んでいましたというだけの話で、特に説明したりなんたりとかいう話ではありません。
できる!Template Haskell (完) - はてな使ったら負けだと思っている deriving Haskell - haskell

これ読みながらポチポチとソース写していくだけでも結構わかりやすくて面白かったです。
前半と後半に練習問題みたいなのがあって、後半のは手を付けてないんだけど、前半のやつはやってみて面白かった。
というのがこのソース。そのまんま解答例なので解きたい人にはアレですが、「難易度:易」だけあって割と簡単でした。
exercises/template_haskell/THex.hs at master · yunomu/exercises · GitHub

で、この例題だと式の生成しかなくって、いや元記事にはインスタンスの導出とかありましたけど、なんか他にネタ無いかなぁと思って作った。

「ncurryを使ってcurry2..curry62という名前の関数を生成しましょう」

これだと宣言を作る事になるので、同レベルの例題としてはちょうどいいかなぁと。メリットもわかりやすいし。いや普通は必要な関数だけ作るでいいと思いますけど。

だいたいこういう感じ。(パッケージ読み込みは略)

% ghci -XTemplateHaskell -XQuasiQuotes
GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
Prelude> :m Language.Haskell.TH
Prelude Language.Haskell.TH> runQ [d|curry2 = \f x y -> f (x, y)|]
[ValD (VarP curry2_4) (NormalB (LamE [VarP f_5,VarP x_6,VarP y_7] (AppE (VarE f_5) (TupE [VarE x_6,VarE y_7])))) []]

LamEの節はcurryの定義なのでまあどうでもいいというか。
このやり方で解いたのがこれ。
exercises/template_haskell/Curry.hs at master · yunomu/exercises · GitHub

別解というか、関数宣言なんだからValDじゃなくてFunDを使えよっていうバージョンもあるっていうか、そっちが筋かも。
つまりさっきはcurry2を

curry2 = \f x y -> f (x, y)

と定義したけど、普通に

curry2 f x y = f (x, y)

って書けよという話もあると思うので、そっちでやってみるとそれはそれでパターンマッチの構造とかわがわかって面白いかもしれません。

だいたいこんなかんじ。

Prelude Language.Haskell.TH> runQ [d|curry2 f x y = f (x, y)|]
[FunD curry2_8 [Clause [VarP f_9,VarP x_10,VarP y_11] (NormalB (AppE (VarE f_9) (TupE [VarE x_10,VarE y_11]))) []]]

さしあたって私は使い道が無かったので遊んでみたという話でした。