QuasiQuotation für lesbareren Atom-Code verwenden
In letzter Zeit habe ich experimentiert, Atom zur Generierung von intrinsisch zuverlässiger eingebetteter Software zu verwenden.
Im Gegensatz zu Alternativen wie Copilot wird bei Atom der umgebende C-Code als Haskell-Strings eingebettet, während andere Konzepte einfach einen Satz von C-Dateien generieren, die in deinen Hauptcode eingebunden werden.
Das Einbinden des C-Codes als String führt jedoch zu Konstrukten wie diesem:
footer :: String
footer = unlines [
"void setup() {",
" pinMode(ledPin, OUTPUT);",
"}"]Beim Modifizieren des C-Codes musst du immer darauf achten, dass nicht nur der C-Code selbst korrekt sein muss, sondern auch das Listen-von-Zeilen-Konstrukt, das ihn umgibt, gepflegt werden muss.
Ich glaube, dass dies fehleranfällig ist, und selbst wenn Editoren dich dabei unterstützen könnten, leidet der Code an mangelnder Zuverlässigkeit.
Mit GHCs QuasiQuotation, wie auf dem HaskellWiki, dem FPComplete TemplateHaskell 101 und dem FPComplete QuasiQuotation 101 beschrieben, habe ich eine einfache Lösung gefunden, um reinen C-Code in Haskell als Strings einzubetten.
Voraussetzungen:**
Zuerst musst du ein Haskell-Modul hinzufügen, das den QuasiQuoter für String-Einbettung bereitstellt (beachte, dass GHC es nicht erlaubt, den QuasiQuoter lokal zu deklarieren, daher muss er in einem separaten Modul sein).
Anerkennung geht an den Autor des FPComplete TH 101 für ein Beispiel, das mir beim Einstieg geholfen hat. Sein Code erzeugt jedoch eine Warnung über einige undefinierte Felder von QuasiQuoter, daher habe ich undefined für besagte Felder hinzugefügt.
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 embedStrC-Code inline einbinden:
Um Inline-C-Code zu verwenden, importiere einfach StringEmbed und schließe deinen C-Code dann so ein: [embedStr|...|]
{-# LANGUAGE QuasiQuotes #-}
import StringEmbed
cFooter :: String
cFooter = [embedStr|
void setup() {
pinMode(ledPin, OUTPUT);
}
|]C-Code aus externen Dateien einbinden:
Wenn du große Mengen an C-Code einbinden musst, erwäge die Möglichkeit, ihn aus externen Dateien einzubinden, um den Code übersichtlicher zu gestalten. StringEmbed.hs enthält bereits embedStringFile, das dir diese Möglichkeit einfach nutzbar macht:
{-# LANGUAGE QuasiQuotes #-}
import StringEmbed
cFooter :: String
cFooter = [embedStrFile|code.c|]Eine alternative Möglichkeit wäre die Verwendung von embedFile aus der file-embed-Bibliothek:
{-# LANGUAGE TemplateHaskell #-}
import Data.FileEmbed
import Data.ByteString (ByteString)
cFooter :: ByteString
cFooter = $(embedFile "code.c")Beachte, dass embedFile ein ByteString anstelle eines String zurückgibt. Während dies generell effizienter ist, ist bei Atom nur die Kompilationseffizienz betroffen und du musst den ByteString ohnehin in einen String konvertieren, um ihn in Atom zu verwenden.