7

我发现自己写了很多代码,比如

putStr "foo (bar 1) (bar 2) =
打印 $ foo (bar 1) (bar 2)

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

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

有人知道我将如何编写一个接受 aString并从中生成上述代码的函数吗?我猜这应该很容易,但是没有很好的文档记录。

4

5 回答 5

10

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)|]
于 2012-02-29T13:13:07.213 回答
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))
于 2012-02-29T13:09:53.700 回答
1

这里有一个eval使用 GHC API 的类似 Haskell 代码的示例。

于 2012-02-29T12:14:14.537 回答
0

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

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

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

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

  1. 没有人真正需要这个功能。(好吧,显然除了我。)

  2. 它并不像看起来那么微不足道。(例如,也许搞清楚在什么上下文中解析表达式有点繁琐?)

于 2012-02-29T13:19:46.950 回答
0

您还可以使用为处理此确切用例而编写的转储包:

{-# 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 包管理器的一部分),您甚至可以使用这个“单线”快速尝试:

$ 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"

于 2016-09-25T15:52:32.063 回答