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 の使い方はソースに書いた。
少し修正して、毎回Reify.hsを生成しないようになった。spliceのことをすっかりわすれていた…
module Main where import Control.Monad.Trans (liftIO) import Data.List -- from hint import qualified Language.Haskell.Interpreter as H import Language.Haskell.Interpreter.Unsafe -- template haskell import Language.Haskell.TH as TH import System.Environment -- 他の環境で動かす時はここを変える confs = ["/opt/local/lib/ghc-6.10.4/package.conf", "/Users/keigoi/.ghc/i386-darwin-6.10.4/package.conf"] main = do [mod,typname] <- getArgs fieldsTest mod typname fieldsTest mod typname = do res <- H.runInterpreter $ fields mod typname print res fields mod typname = do myLoadModule ["Meta", mod] H.interpret ("$( meta ''"++typname++")") (H.as :: TH.Info) myLoadModule :: [String] -> H.Interpreter () myLoadModule mods = do H.reset unsafeSetGhcOption ("-i"++concat (intersperse ":" confs)) H.set [H.languageExtensions H.:= [H.ImplicitParams, H.TemplateHaskell]] H.set [H.installedModulesInScope H.:= True] H.loadModules $ map (++".hs") mods H.setTopLevelModules mods H.setImportsQ [("Prelude", Nothing)]