ローカル変数の一覧を取得
Posted feedbacks - Haskell
Haskellでは実行時に変数を操作する手段がないので、Template Haskellでコンパイル時にコードを挿入することで対処します。力ずくです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | {-# OPTIONS_GHC -fth #-}
module LocalVariables(localVariables, withLocalVariables) where
import Control.Monad
import Data.Dynamic
import Language.Haskell.TH
import qualified Data.Map as Map
localVariables :: Map.Map String Dynamic
localVariables = error "localVariables outside withLocalVariables"
localVariablesName :: Name
localVariablesName = 'localVariables
withLocalVariables :: Q [Dec] -> Q [Dec]
withLocalVariables = (>>=trDecs)
where
trDecs decs = mapM (trDec []) decs
trDec vars dec = case dec of
FunD name cls -> liftM (FunD name) $ mapM (trClause vars) cls
ValD pat body decs -> do
decs' <- mapM (trDec vars) decs
body' <- trBody (vars ++ concatMap decVars decs) body
return $ ValD pat body' decs'
_ -> return dec
trClause vars (Clause pats body decs) = do
decs' <- mapM (trDec vars) decs
body' <- trBody (vars ++ concatMap patVars pats ++ concatMap decVars decs) body
return $ Clause pats body' decs'
trBody vars (GuardedB ges) = liftM GuardedB $ mapM trGuarded ges
where
trGuarded (guard, exp) = do
exp' <- trExp vars exp
return (guard, exp')
trBody vars (NormalB exp) = liftM NormalB $ trExp vars exp
trExp :: [Name] -> Exp -> Q Exp
trExp vars exp = case exp of
VarE name
| name == localVariablesName -> replacement vars
| otherwise -> return exp
AppE x y -> liftM2 AppE (rec x) (rec y)
InfixE x op y -> liftM3 InfixE (maybeMapM rec x) (rec op) (maybeMapM rec y)
LamE pats e -> liftM (LamE pats) $ trExp (vars ++ concatMap patVars pats) e
TupE es -> liftM TupE $ mapM rec es
CondE c t f -> liftM3 CondE (rec c) (rec t) (rec f)
LetE decs e -> do
decs' <- mapM (trDec vars) decs
e' <- trExp (vars ++ concatMap decVars decs) e
return $ LetE decs' e'
CaseE e matches -> liftM2 CaseE (rec e) (mapM (trMatch vars) matches)
DoE ss -> liftM DoE $ trStmts vars ss
CompE ss -> liftM CompE $ trStmts vars ss
ArithSeqE rng -> liftM ArithSeqE $ trRange vars rng
ListE es -> liftM ListE $ mapM rec es
SigE e tp -> liftM2 SigE (rec e) (return tp)
RecConE name fexps -> liftM (RecConE name) $ mapM (trFexp vars) fexps
RecUpdE e fexps -> liftM2 RecUpdE (rec e) (mapM (trFexp vars) fexps)
_ -> return exp
where
rec e = trExp vars e
maybeMapM f Nothing = return Nothing
maybeMapM f (Just v) = liftM Just $ f v
trFexp vars (name, e) = do
e' <- trExp vars e
return (name, e')
trRange vars rng = case rng of
FromR x -> liftM FromR $ tr x
FromThenR x y -> liftM2 FromThenR (tr x) (tr y)
FromToR x y -> liftM2 FromToR (tr x) (tr y)
FromThenToR x y z -> liftM3 FromThenToR (tr x) (tr y) (tr z)
where
tr e = trExp vars e
trStmts :: [Name] -> [Stmt] -> Q [Stmt]
trStmts vars [] = return []
trStmts vars (stmt:rest) = case stmt of
BindS pat exp -> let vars' = vars ++ patVars pat
in liftM2 (:) (liftM (BindS pat) (trExp vars' exp)) (trStmts vars' rest)
LetS decs -> liftM2 (:) (liftM LetS $ mapM (trDec vars) decs) (trStmts (vars ++ concatMap decVars decs) rest)
NoBindS exp -> liftM2 (:) (liftM NoBindS $ trExp vars exp) (trStmts vars rest)
ParS _ -> error "ParS: what's this?"
trMatch vars (Match pat body decs) = do
decs' <- mapM (trDec vars) decs
body' <- trBody (vars ++ patVars pat ++ concatMap decVars decs) body
return $ Match pat body' decs'
decVars dec = case dec of
FunD name _ -> [name]
ValD pat _ _ -> patVars pat
_ -> []
patVars pat = case pat of
VarP name -> [name]
TupP pats -> concatMap patVars pats
ConP name pats -> concatMap patVars pats
InfixP p0 _ p1 -> patVars p0 ++ patVars p1
TildeP p -> patVars p
AsP name p -> name : patVars p
RecP _ fps -> concatMap fpatVars fps
ListP pats -> concatMap patVars pats
_ -> []
fpatVars (_, pat) = patVars pat
replacement vars = [| Map.fromList $(list) |]
where
list = listE $ map entry vars
entry name = [| ($(str), toDyn $(obj)) |]
where
str = return $ LitE $ StringL $ show name
obj = return $ VarE name
{-
使い方:
別モジュールで
import LocalVariables
$(withLocalVariables [d|
foo :: IO (Map.Map String Dynamic)
foo = do
let x = 1 :: Int
let y = "hello"
return localVariables
|])
のようにすると、localVariablesの出現を置換した上でfooが定義される
-}
|


にしお
#3391()
Rating0/0=0.00
Pythonで表現すると、下のコードの???部分を埋めることになります。
>>> def foo(): x = 1 y = "hello" ??? return result >>> foo() {'y': 'hello', 'x': 1}[ reply ]