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

putStr "foo (bar 1) (bar 2) ="
print $ foo (bar 1) (bar 2)
.

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

一种方法是将所有文本放在文件中,并编写一个读取文件的小程序,并从中生成Haskell源代码。但另一种替代方案是使用模板haskell。

是否有人知道如何编写一个函数,该函数采用一个生成的函数,并从中生成上面的代码?我猜这应该很容易,但是没有充分记录。

有帮助吗?

解决方案

可以使用 haskell-src-meta 包来解析Haskell代码。这是一个快速示例,你如何将其与模板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)|]
.

其他提示

模板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))
.

使用ghc api 在这里 <。

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

我期望有一个函数将字符串变成表达式,但显然没有存在这样的功能。甚至没有一个函数来从磁盘加载更多源代码。所以这似乎这项任务实际上是不可能的!我很惊讶。

我可以做的最接近的事情是引用我想要运行的表达式,然后在运行它之前构建漂亮打印引用的表达式的拼接。然而,这让我怜悯GHC的表达式漂亮的打印机。当我键入它时,标签不会完全出来。 (特别是,它似乎用完全合格的名称替换运营商,这只是痛苦。)

你会认为这样的功能会非常微不足道。因此,它没有实现的事实只能归因于两件事之一:

  1. 没有人实际上需要这个功能。 (很好,除了我,显然是。)

  2. 它不会像看起来一样微不足道。 (例如,也许弄清楚解析表达式的上下文某种情况以某种方式而言?)

您也可以使用转储包编写的包装以处理此确切用例:

{-# 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"

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top