Using QuasiQuotation for more readable Atom code
Lately I’ve been experimenting with using Atom to generate intrinsically reliable embedded software.
In contrast to alternatives like Copilot in Atom you include the surrounding C code as Haskell strings whereas other concepts just generate a set of C files to include in your main code.
However, inclusion of the C code as String
leads to constructs like this:
footer :: String
footer = unlines [
"void setup() {",
" pinMode(ledPin, OUTPUT);",
"}"]
When modifying the C code you always have to take care that not only the C code itself needs to be correct, but you also have to maintain the list-of-lines construct surrounding it.
I believe this is prone to errors and even if editors might support you in doing so, the code suffers from lack of reliability.
Using GHC’s QuasiQuotation as described on the HaskellWiki, the FPComplete TemplateHaskell 101 and the FPComplete QuasiQuotation 101 I found an easy solution for including pure C code in Haskell as Strings.
Prerequisites:**
First, you need to add a Haskell module providing the QuasiQuoter
for string embedding (note that GHC disallows the QuasiQuoter
to be declared locally, so it needs to be in a separate module).
Credits go to the Author of the FPComplete TH 101 for providing an example that helped me getting started. His code, however, produces a warning about some fields of QuasiQuoter
being undefined, therefore I added undefined
for said fields.
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
Including C code inline:
In order to use inline C code, simply import StringEmbed
and then enclose your C code like this: [embedStr|...|]
{-# LANGUAGE QuasiQuotes #-}
import StringEmbed
cFooter :: String
cFooter = [embedStr|
void setup() {
pinMode(ledPin, OUTPUT);
}
|]
Including C code from external files:
If you have large amounts of C code to include, consider the possibility of including it from external files to un-clutter the code. StringEmbed.hs
already contains embedStringFile
allowing you to easily use this possibility:
{-# LANGUAGE QuasiQuotes #-}
import StringEmbed
cFooter :: String
cFooter = [embedStrFile|code.c|]
An alternative way would be to use embedFile
from the file-embed
library:
{-# LANGUAGE TemplateHaskell #-}
import Data.FileEmbed
import Data.ByteString (ByteString)
cFooter :: ByteString
cFooter = $(embedFile "code.c")
Note that embedFile
yields a ByteString
instead of a String
. While this is generally more efficient, in case of Atom only the compilation efficiency is affected and you have to convert the ByteString
to String
anyway in order to use it in Atom.