使用 QuasiQuotation 使 Atom 代码更易读
最近我一直在试验使用 Atom 生成内在可靠的嵌入式软件。
与 Copilot 等替代方案相比,在 Atom 中你将周围的 C 代码作为 Haskell 字符串包含,而其他概念只是生成一组 C 文件以包含在你的主代码中。
然而,将 C 代码作为 String 包含会导致这样的结构:
footer :: String
footer = unlines [
"void setup() {",
" pinMode(ledPin, OUTPUT);",
"}"]修改 C 代码时,你总是必须注意不仅 C 代码本身需要正确,而且你还必须维护围绕它的行列表结构。
我相信这容易出错,即使编辑器可能支持你这样做,代码也缺乏可靠性。
使用 GHC 的 QuasiQuotation,如 HaskellWiki、FPComplete TemplateHaskell 101 和 FPComplete QuasiQuotation 101 中所述,我找到了一个将纯 C 代码作为字符串包含在 Haskell 中的简单解决方案。
前提条件:**
首先,你需要添加一个提供字符串嵌入 QuasiQuoter 的 Haskell 模块(注意 GHC 不允许在本地声明 QuasiQuoter,所以它需要在单独的模块中)。
功劳归于 FPComplete TH 101 的作者提供了帮助我入门的示例。然而,他的代码产生了关于 QuasiQuoter 某些字段未定义的警告,因此我为这些字段添加了 undefined。
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|...|]
{-# LANGUAGE QuasiQuotes #-}
import StringEmbed
cFooter :: String
cFooter = [embedStr|
void setup() {
pinMode(ledPin, OUTPUT);
}
|]从外部文件包含 C 代码:
如果你有大量 C 代码要包含,考虑从外部文件包含以使代码更整洁。StringEmbed.hs 已包含 embedStringFile,允许你轻松使用此功能:
{-# LANGUAGE QuasiQuotes #-}
import StringEmbed
cFooter :: String
cFooter = [embedStrFile|code.c|]另一种方法是使用 file-embed 库中的 embedFile:
{-# LANGUAGE TemplateHaskell #-}
import Data.FileEmbed
import Data.ByteString (ByteString)
cFooter :: ByteString
cFooter = $(embedFile "code.c")注意 embedFile 产生 ByteString 而不是 String。虽然这通常更高效,但在 Atom 的情况下只影响编译效率,而且你必须将 ByteString 转换为 String 才能在 Atom 中使用它。