TemplateHaskellでちょっと理解したことをメモ
ここんところTemplateHaskellでコケてしまったので忘れないうちにメモTemplateHaskellとは
Haskellのドキュメントには「Template Haskell is a GHC extension to Haskell that adds compile-time metaprogramming facilities.」ってかかれてて、GHCのextensionでコンパイルのメタプログラミングを出きるようにする機能らしいが何が何だか解らないので簡単なところで調べてみたマクロみたいなもので抽象構文木(AST)として記述するとか説明があるけどよく解らない...笑
ghciから使ってみる
ghciを起動するときに-XTemplateHaskellってオプションをつけて起動するか、ghciを起動した後に:setで足してもいい、それからモジュールを読み込むGHCi, version 7.4.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Prelude> Prelude> :set -XTemplateHaskell Prelude> :m +Language.Haskell.TH Prelude Language.Haskell.TH>
いきなり使ってみるが...Q Expがshowのinstanceでないので叱られる始末
Prelude Language.Haskell.TH> [e|1+1|] <interactive>:15:1: No instance for (Show (Q Exp)) arising from a use of `print' Possible fix: add an instance declaration for (Show (Q Exp)) In a stmt of an interactive GHCi command: print it Prelude Language.Haskell.TH>
とりあえずこいつの型を調べてみると、Q Expってなに?そもそもQってなに?
Prelude Language.Haskell.TH> :t [e|1+1|] [e|1+1|] :: Q Exp Prelude Language.Haskell.TH>
ここでとりあえずモジュール一覧を確認、見たところでほとんど解らない...何かQはnewtypeされててMonadクラスとFunctorのクラスのinstanceになってるらしい
Prelude Language.Haskell.TH> :browse data Body = GuardedB [(Guard, Exp)] | NormalB Exp type BodyQ = Q Body data Callconv = CCall | StdCall data Clause = Clause [Pat] Body [Dec] type ClauseQ = Q Clause data Con = NormalC Name [Language.Haskell.TH.Syntax.StrictType] | RecC Name [Language.Haskell.TH.Syntax.VarStrictType] ... ... newtype Q a = Language.Haskell.TH.Syntax.Q { Language.Haskell.TH.Syntax.unQ :: forall (m :: * -> *).Language.Haskell.TH.Syntax.Quasi m => m a} instance Monad Q -- Defined in `Language.Haskell.TH.Syntax' instance Functor Q -- Defined in `Language.Haskell.TH.Syntax' ... ... ...
次にExpってなにか調べてみる
Prelude Language.Haskell.TH> :i Exp data Exp = VarE Name | ConE Name | LitE Lit | AppE Exp Exp | InfixE (Maybe Exp) Exp (Maybe Exp) | UInfixE Exp Exp Exp | ParensE Exp | LamE [Pat] Exp | TupE [Exp] | UnboxedTupE [Exp] | CondE Exp Exp Exp | LetE [Dec] Exp | CaseE Exp [Match] | DoE [Stmt] | CompE [Stmt] | ArithSeqE Range | ListE [Exp] | SigE Exp Type | RecConE Name [FieldExp] | RecUpdE Exp [FieldExp] -- Defined in `Language.Haskell.TH.Syntax' instance Eq Exp -- Defined in `Language.Haskell.TH.Syntax' instance Show Exp -- Defined in `Language.Haskell.TH.Syntax' instance Ppr Exp -- Defined in `Language.Haskell.TH.Ppr' Prelude Language.Haskell.TH>
なるほどExpとはそんな感じなのね、って解らない、挫折寸前でこのサイトを見つける「Basic Tutorial of Template Haskell」そこには4つほどタイプがあって
- Expression quotations
- Declaration quotations
- Type quotations
- Pattern quotations
パターンって2回言った...で使ってたのがExpression quotationってやつ、で調べている過程でrunQっていう関数を発見、こいつは上のquotationを展開してくれるっぽい
runQで色々やってみる
[e|1+1|]はQ Expでイケそうな気がするPrelude Language.Haskell.TH> :i runQ runQ :: Language.Haskell.TH.Syntax.Quasi m => Q a -> m a -- Defined in `Language.Haskell.TH.Syntax' Prelude Language.Haskell.TH> Prelude Language.Haskell.TH> runQ [e|1+1|] Loading package array-0.4.0.0 ... linking ... done. Loading package deepseq-1.3.0.0 ... linking ... done. Loading package containers-0.4.2.1 ... linking ... done. Loading package pretty-1.1.1.0 ... linking ... done. Loading package template-haskell ... linking ... done. InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) (Just (LitE (IntegerL 1))) Prelude Language.Haskell.TH> Prelude Language.Haskell.TH> let ex = [e|1+1|] Prelude Language.Haskell.TH> :t ex ex :: Q Exp Prelude Language.Haskell.TH> Prelude Language.Haskell.TH> $(st) 2 Prelude Language.Haskell.TH> Prelude Language.Haskell.TH> :t InfixE InfixE :: Maybe Exp -> Exp -> Maybe Exp -> Exp Prelude Language.Haskell.TH>で$()で実行されるのか
もうちょいいじってみる
Prelude Language.Haskell.TH> Prelude Language.Haskell.TH> $(return (InfixE (Just (LitE (IntegerL 1))) (VarE (mkName "+")) (Just (LitE (IntegerL 1))))) 2 Prelude Language.Haskell.TH>
Qモナド?辺りに包んで返せば同じ結果になる、要するにコンパイル時にソースコードに掛かれた[|expression|]を展開してくれる機能なのか、ちなみに[]は「オックスフォード角括弧」って言うらしい、覚えきれない、難しいのですが他のタイプも調べてみます。
0 件のコメント:
コメントを投稿