我发现自己写了很多像
这样的代码putStr "foo (bar 1) (bar 2) =" print $ foo (bar 1) (bar 2)
问题是,打印的消息可能与实际执行的代码不同步。显而易见的解决方案是自动生成此代码。
这样做的一种方法是将所有文本放在一个文件中,然后编写一个小程序来读取文件并从中生成Haskell源代码。但另一种选择是使用Template Haskell。
有人知道如何编写一个带String
的函数并从中生成上述代码吗?我猜它应该很容易,但TH没有很好的记录。
答案 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表达漂亮打印机的摆布。当我输入标签时,标签不会出现完全。 (特别是,似乎用完全限定的名称替换运算符,这很痛苦。)
你会认为这样的功能实现起来非常简单。因此,未实施的事实只能归因于以下两点之一:
实际上没有人需要此功能。 (好吧,除了我,显然。)
它并不像它看起来那么微不足道。 (例如,可能会弄清楚解析表达式的上下文是多么的狡猾?)
答案 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"