hint 使って型推論、 haskell-src-exts を使って Parse
GHC API のラッパー hint を使えばHaskellでも手軽にリフレクションっぽいことができる。例えば動的にHaskellのソースをロードして関数を呼び出す、といったことが数行で書ける。
ところでリフレクション周りで色々やろうとすると、すぐに関数の型を解析したくなるのだけど、hint は 型推論の結果を文字列で返すのでHaskell的には使いにくい。情けない話だが、どうやら GHC APIがそうなっているので仕方ないようだ。Haskellのパーサが必要になる。
haskell-src-exts を使えば、Haskellの型をparse できる。
似たようなことをやる方法に template haskellとか haskell-src という標準のアレがあるけれど、いくつかの拡張構文をサポートしていない。その点 haskell-src-exts はほぼすべての GHC拡張をサポートしている。
というわけで、hint と haskell-src-exts を組み合わせて 型を解析する前段階までを作ってみた。
ちょっとした目的にはややオーバーキルだけど、まあしょうがない。
使い方
Haskellのモジュールと関数名を指定すると、関数の型を推論して表示してくれる。(ついでに haskell-src-exts でパースした結果も出す。)
Impricit Parameters を使った関数の型も出せる。
$ cat IParamExample.hs {-# LANGUAGE ImplicitParams, NoMonomorphismRestriction #-} module IParamExample where test = ?a ++ ?b $ ./Main IParamExample test Right ("(?b::[a], ?a::[a]) => [a]",TyForall Nothing [IParam (IPDup "b") (TyList (TyVar (Ident "a"))),IParam (IPDup "a") (TyList (TyVar (Ident "a")))] (TyList (TyVar (Ident "a"))))
ソース
hint から Template Haskell を使って reify
下の記事の続き。
hintは意外に私の欲しい機能がなく、たとえば「あるレコード型のフィールド名をすべて取得」みたいなことができない。Template Haskell の reify 関数なら、任意の識別子について reify が使えるののに…
というわけで、 ややアクロバティックだけど、Template Haskellでreifyしたデータを、 hintのinterpret関数でこちら側にもってくる、というコードを書いた。
必要なライブラリはhintのみ。
使い方
まず、下で説明する Meta.hs をカレントに置く。
下の Foo.hs から
module Foo where data Point = Point {x::Int, y::Int}
Point型の情報を取り出して、そのままprintする.
$ ./Main Foo Point Right (TyConI (DataD [] Foo.Point [] [RecC Foo.Point [(Foo.x,NotStrict,ConT GHC.Types.Int),(Foo.y,NotStrict,ConT GHC.Types.Int)]] []))
ソース
レイフィケーションのために Meta.hs というのを準備した。以前このブログにアップした気がするのだけど見つからないので再掲。
Meta.hs の使い方はソースに書いた。