使用 QuasiQuotation 使 Atom 代码更易读

最近我一直在试验使用 Atom 生成内在可靠的嵌入式软件。

Copilot 等替代方案相比,在 Atom 中你将周围的 C 代码作为 Haskell 字符串包含,而其他概念只是生成一组 C 文件以包含在你的主代码中。

然而,将 C 代码作为 String 包含会导致这样的结构:

footer.hs
footer :: String
footer = unlines [
          "void setup() {",
          "  pinMode(ledPin, OUTPUT);",
          "}"]

修改 C 代码时,你总是必须注意不仅 C 代码本身需要正确,而且你还必须维护围绕它的行列表结构。

我相信这容易出错,即使编辑器可能支持你这样做,代码也缺乏可靠性。

使用 GHC 的 QuasiQuotation,如 HaskellWikiFPComplete TemplateHaskell 101FPComplete QuasiQuotation 101 中所述,我找到了一个将纯 C 代码作为字符串包含在 Haskell 中的简单解决方案。

前提条件:**

首先,你需要添加一个提供字符串嵌入 QuasiQuoter 的 Haskell 模块(注意 GHC 不允许在本地声明 QuasiQuoter,所以它需要在单独的模块中)。

功劳归于 FPComplete TH 101 的作者提供了帮助我入门的示例。然而,他的代码产生了关于 QuasiQuoter 某些字段未定义的警告,因此我为这些字段添加了 undefined

StringEmbed.hs
module StringEmbed(embedStr, embedStrFile) where

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

embedStr :: QuasiQuoter
embedStr = QuasiQuoter { quoteExp = stringE,
                    quotePat = undefined,
                    quoteDec = undefined,
                    quoteType = undefined }

embedStrFile :: QuasiQuoter
embedStrFile = quoteFile embedStr

内联包含 C 代码:

要使用内联 C 代码,只需 import StringEmbed 然后像这样包围你的 C 代码:[embedStr|...|]

example_embed.hs
{-# LANGUAGE QuasiQuotes #-}

import StringEmbed

cFooter :: String
cFooter = [embedStr|

void setup() {
  pinMode(ledPin, OUTPUT);
}

|]

从外部文件包含 C 代码:

如果你有大量 C 代码要包含,考虑从外部文件包含以使代码更整洁。StringEmbed.hs 已包含 embedStringFile,允许你轻松使用此功能:

example_embed_file.hs
{-# LANGUAGE QuasiQuotes #-}

import StringEmbed

cFooter :: String
cFooter = [embedStrFile|code.c|]

另一种方法是使用 file-embed中的 embedFile

example_embed_file_th.hs
{-# LANGUAGE TemplateHaskell #-}
import Data.FileEmbed
import Data.ByteString (ByteString)

cFooter :: ByteString
cFooter = $(embedFile "code.c")

注意 embedFile 产生 ByteString 而不是 String。虽然这通常更高效,但在 Atom 的情况下只影响编译效率,而且你必须将 ByteString 转换为 String 才能在 Atom 中使用它。


Check out similar posts by category: Haskell