Type Safe Printf
をTHで実装してみる的な。
printfの型無視バージョンの実装は普通こんな感じになります
printf fmt = pr fmt "" where pr :: String -> String -> なんか色々入るの pr "" res = PRes res pr ('%':'d':cs) res = (\i -> pr cs (res ++ show (i::Int))) pr ('%':'s':cs) res = (\str -> pr cs (res ++ str)) pr (c:cs) res = (pr cs (res ++ [c]))
適切に引数を順次適用していけるように、包みまくっとくというわけです。
これと全く同じスタイルでTHでも書けます。
ts_printf format = gen (parse_format format) [| "" |] gen [] code = code gen (D : res) code = [| \d -> $(gen res [| $code ++ show (d::Int) |]) |] -- dの型でIntを要求するようにする。 gen (S : res) code = [| \s -> $(gen res [| $code ++ s |]) |] gen (Lit char :res) code= gen res [| $code ++ [char] |] parse_format "" = [] parse_format ('%':'d':res) = D : parse_format res parse_format ('%':'s':res) = S : parse_format res parse_format (char:res) = (Lit char) : parse_format res
*Th_Printf> :t $(ts_printf "%d %d %s hogera %s") $(ts_printf "%d %d %s hogera %s") :: Int -> Int -> [Char] -> [Char] -> [Char]
それと、Template Haskellでlambda式にtype signatureを与える事はまだ出来ないようです。
例えば
{-# LANGUAGE PatternSignatures #-} gen (D : res) code = [| \(d::Int) -> $(gen res [| $code ++ show d |]) |]
とかすると。
Type signatures in patterns not (yet) handled by Template Haskell d :: Int
ですって。