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

ですって。