Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedding long strings inline without using lists

For several projects I need to embed long strings into Haskell source code.

The obvious way to do this is to unlines a list of lines. However, reading and maintaining this is cumbersome.

cCode :: String
cCode = unlines [
          "int main(int argc*, char** argv)",
          "  doStuff();",
          "}"]

Is there any way you can embed strings without any overhead (like the list as shown above) or even files? Is TemplateHaskell/Quasi-quotation the way to go here?

Note: This question was answered in Q&A form. Therefore it does not show any research effort.

like image 880
Uli Köhler Avatar asked Feb 13 '23 08:02

Uli Köhler


2 Answers

It is possible using QuasiQuotation as described in this blogpost written by me.

Step 1: Create a module (we'll call it StringEmbed.hs that contains the required functions

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

Note because of TH peculiarities it is not possible to just copy those functions into the module where you use them.

Step 2a: In your module, embed your strings:

{-# LANGUAGE QuasiQuotes #-}

import StringEmbed

cCode :: String
cCode = [embedStr|
int main(int argc, char** argv) {
    doStuff();
}
|]

Note that you only have to add the QuasiQuotes LANGUAGE pragma. TemplateHaskell is not required for this technique.

Because QuasiQuotes are delimited using |], you can't use that character sequence anywhere in the quasi-quoted string.

Step 2b: You can just as easily embed a file. Let's assume the file code.c contains the string you intend to embed.

{-# LANGUAGE QuasiQuotes #-}

import StringEmbed

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

Alternatively you can use one of the many haskell libraries instead of StringEmbed.hs, for example heredoc (thanks Ørjan Johansen for the tip!)

{-# LANGUAGE QuasiQuotes #-}
import Text.Heredoc


cCode :: String
cCode = [here|
int main(int argc, char** argv) {
    doStuff();
}
|]
like image 199
Uli Köhler Avatar answered Feb 24 '23 07:02

Uli Köhler


You can use multi-line strings with gaps. It might not be as smooth as the quasiquoters, but it is standard Haskell.

cCode :: String
cCode = "\
      \int main(int argc*, char** argv)\n\
      \  doStuff();\n\
      \}\n\
      \"
like image 32
augustss Avatar answered Feb 24 '23 05:02

augustss