打印并执行字符串

时间:2012-02-29 11:52:38

标签: haskell template-haskell

我发现自己写了很多像

这样的代码
putStr "foo (bar 1) (bar 2) ="
print $ foo (bar 1) (bar 2)

问题是,打印的消息可能与实际执行的代码不同步。显而易见的解决方案是自动生成此代码。

这样做的一种方法是将所有文本放在一个文件中,然后编写一个小程序来读取文件并从中生成Haskell源代码。但另一种选择是使用Template Haskell。

有人知道如何编写一个带String的函数并从中生成上述代码吗?我猜它应该很容易,但TH没有很好的记录。

5 个答案:

答案 0 :(得分:8)

您可以使用haskell-src-meta包解析Haskell代码。以下是如何将其与Template Haskell结合使用的快速示例。

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.Meta

runShow = QuasiQuoter
    { quoteExp  = runShowQQ
    , quotePat  = undefined
    , quoteType = undefined
    , quoteDec  = undefined
    }

runShowQQ :: String -> Q Exp
runShowQQ s = do
    let s'          = s ++ " = "
        Right exp = parseExp s
        printExp  = appE [|print|] (return exp)
    infixApp [|putStr s'|] [|(>>)|] printExp

你会像这样使用它

{-# LANGUAGE QuasiQuotes #-}

[runShow|foo (bar 1) (bar 2)|]

答案 1 :(得分:4)

模板Haskell不提供解析任意字符串的简单方法,因此最简单的解决方案可能是使用C预处理器。但是,GHC中的内置函数不支持字符串化,因此我们需要传递额外的选项来使用“真正的”字符串。

{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -pgmP cpp #-}

#define PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x))

然后您可以像这样使用它:

PRINT_EXP(foo (bar 1) (bar 2))

答案 2 :(得分:1)

有一个eval示例 - 就像使用GHC API {Htmlell代码} {/ 3}}一样。

答案 3 :(得分:0)

哎哟。我这很容易,但据我所知,实际上不可能

我期待有一个将字符串转换为表达式的函数,但显然不存在这样的函数。甚至没有从磁盘加载更多源代码的功能。所以看来这个任务实际上是不可能的!我对此感到非常惊讶。

我能做的最接近的事情是引用我想要运行的表达式,然后构建一个拼接,在运行它之前打印出引用的表达式。然而,这让我受到了GHC表达漂亮打印机的摆布。当我输入标签时,标签不会出现完全。 (特别是,似乎用完全限定的名称替换运算符,这很痛苦。)

你会认为这样的功能实现起来非常简单。因此,未实施的事实只能归因于以下两点之一:

  1. 实际上没有人需要此功能。 (好吧,除了我,显然。)

  2. 它并不像它看起来那么微不足道。 (例如,可能会弄清楚解析表达式的上下文是多么的狡猾?)

答案 4 :(得分:0)

您也可以使用编写的dump包来处理这个确切的用例:

{-# language QuasiQuotes #-}
import Debug.Dump

main = putStrLn [d| foo (bar 1) (bar 2) |]

foo = (+)
bar = (+1)

打印:(foo (bar 1) (bar 2)) = 5

它还处理多个以逗号分隔的表达式:

putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |]

打印:(foo (bar 1) (bar 2)) = 5 (map bar [1, 2]) = [2,3]

加成: 如果您安装了nix-shell(nix package manager的一部分),您甚至可以使用这个" one-liner":

快速尝试

$ nix-shell -p "nix-shell -p "haskellPackages.ghcWithPackages (p: [p.dump])" --run "echo '{-# language QuasiQuotes #-}; import Debug.Dump; foo = (+); bar = (+1); main = putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |]' | runhaskell"