qm-interpolated-string: Implementation of interpolated multiline strings

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.


Implementation of interpolated multiline strings that ignores indentation and trailing whitespaces

[Skip to ReadMe]


Change logCHANGELOG.md
Dependenciesbase (==4.*), bytestring (==0.10.*), haskell-src-meta (>=0.3 && <0.9), template-haskell (>=2.5 && <3), text (==1.*) [details]
CopyrightViacheslav Lotsmanov
AuthorViacheslav Lotsmanov
MaintainerViacheslav Lotsmanov <lotsmanov89@gmail.com>
Home pagehttps://github.com/unclechu/haskell-qm-interpolated-string
Source repositoryhead: git clone git://github.com/unclechu/haskell-qm-interpolated-string.git
UploadedSun Feb 4 23:29:25 UTC 2018 by unclechu




Maintainers' corner

For package maintainers and hackage trustees

Readme for qm-interpolated-string-

[back to package description]


Hackage Build Status

Implementation of interpolated multiline string QuasiQuoter that ignores indentation and trailing whitespaces.

Actually it's modification of interpolatedstring-perl6 package. I've forked it to implemenent my own strings I really like.

This implementation based on qc from interpolatedstring-perl6 package but ignores any indentation, line breaks (except explicitly written using \n char) and trailing whitespaces.

Write a decoratively formatted string and your decorative indentation and line breaks wont go to result string, but when you really need it, you could just escape it using backslash.

Usage example

{-# LANGUAGE QuasiQuotes #-}

import Text.InterpolatedString.QM

main :: IO ()
main = do
  -- Hello, world! Pi is 3.14…
  putStrLn [qms| Hello,
                 Pi is {floor pi}.{floor $ (pi - 3) * 100}… |]

  -- Some examples with HTML below to demonstrate the difference
  -- between all of the quasi-quoters.

  let title = "Testing"
      text  = "Some testing text"

  -- <article><h1>Testing</h1><p>Some testing text</p></article>
  putStrLn [qm|

  -- <article><h1>{title}</h1><p>{text}</p></article>
  putStrLn [qn|

  -- <article> <h1>Testing</h1> <p>Some testing text</p> </article>
  putStrLn [qms|

  -- <article> <h1>{title}</h1> <p>{text}</p> </article>
  putStrLn [qns|

  -- <article>
  -- <h1>Testing</h1>
  -- <p>Some testing text</p>
  -- </article>
  putStrLn [qmb|

  -- <article>
  -- <h1>{title}</h1>
  -- <p>{text}</p>
  -- </article>
  putStrLn [qnb|


All QuasiQuoters

| QuasiQuoter | Interpolation | Indentation | Line breaks          | Trailing whitespaces |
| qm          | ✓             | Removed     | Removed              | Removed              |
| qn          | ✗             | Removed     | Removed              | Removed              |
| qmb         | ✓             | Removed     | Kept                 | Removed              |
| qnb         | ✗             | Removed     | Kept                 | Removed              |
| qms         | ✓             | Removed     | Replaced with spaces | Removed              |
| qns         | ✗             | Removed     | Replaced with spaces | Removed              |

About naming logic

| Contains in its name | What means                       | QuasiQuoters |
| m                    | Resolves interpolation blocks    | qm, qmb, qms |
| n                    | Without interpolation            | qn, qnb, qns |
| b                    | Keeps line breaks                | qmb, qnb     |
| s                    | Replaces line breaks with spaces | qms, qns     |

About interpolation blocks

Along with all specifics of any of the quoters (which supports interpolation blocks, which has m in their names) interpolation blocks work different. When curly bracket ({) opens everything inside until it closes (by }) is parsed as bare as possible to be given to haskell-src-meta without any modifications, to be parsed as bare haskell code.

But you might need use curly brackets inside an interpolation block. I don't think it would be a good idea, because complicated logic there may cause code readability issues, but if you're sure you need it then you get it. You just need to escape closing bracket to prevent interpolation block from closing, like this: \}. I know it could parsed and opening curly brackets inside could be used to prevent closing by next } symbol, but I chose do it this way to prevent any unobvious tricky behavior (e.g. consider } appear inside a string, [qm|foo {'x':'}':"y"} bar|], how that should be handled?). So I've decided to not make parser to be very smart, just to follow simple logic. You just need to explicitly escape every } symbol inside that isn't closer of an interpolation block (you could find an example below).

About escaping

Symbols that can be escaped

Backslash is used for escaping these:

  1. \n - line break
  2. \ - space (space is supposed to be escaped when you're going to keep some part of indentation)
  3. \↵ - backslash just before end of line cuts off line break (makes sense for qmb, qnb, qms and qns)
  4. \{ - opening bracket of interpolation block (only for qm, qmb and qms, to prevent interpolation and interpret this block as plain text)
  5. \t or \‣ (where is real tab symbol) - tab (escaping it to keep some part of indentation, or if you need tab symbol for some reason, escaping real tabs makes sense only for keeping some part of indentation)
  6. \\ - backslash itself (for situations when you don't want to escape other symbols but just want backslash symbol, \\t, \\n, \\↵, \\{, etc., if backslash doesn't come with any of symbols from this list it is interpreted just as backslash symbol, keep in mind that \\\ (without any of symbols from this list after) and \\\\ are producing same result - \\)
  7. \} - closing bracket inside an interpolation block (it works only inside opened interpolation block) to prevent interpolation block from closing (useful to escape records modification)

Escaping examples

[qm| foo\nbar  |] -- "foo\nbar"
[qm| foo\\nbar |] -- "foo\\nbar"
[qm| foo\tbar  |] -- "foo\tbar"
[qm| foo\\tbar |] -- "foo\\tbar"
[qm| foo\‣bar  |] -- "foo\tbar"   (`‣` is real tab symbol)
[qm| foo\\‣bar |] -- "foo\\\tbar" (`‣` is real tab symbol)
[qm| foo\ bar  |] -- "foo bar"
[qm| foo\\ bar |] -- "foo\\ bar"

[qm| foo\
     bar  |] -- "foobar"
[qm| foo\\
     bar  |] -- "foo\\bar"

[qmb| foo\
      bar  |] -- "foobar"
[qmb| foo\\
      bar  |] -- "foo\\\nbar"

[qm| foo\bar    |] -- "foo\\bar"
[qm| foo\\bar   |] -- "foo\\bar"
[qm| foo\\\bar  |] -- "foo\\\\bar"
[qm| foo\\\\bar |] -- "foo\\\\bar"

More examples

[qm|   you can escape spaces
     \ when you need them    |]
-- Result: "you can escape spaces when you need them"
        indentation and li
  ne bre
   aks are i
-- Result: "indentation and line breaks are ignored"
[qm|  \  You can escape indentation or\n
         line breaks when you need them! \  |]
-- Result: "  You can escape indentation or\nline breaks when you need them!  "
[qm| Interpolation blocks can be escaped too: {1+2} \{3+4} |]
-- Result: "Interpolation blocks can be escaped too: 3 {3+4}"

If you don't need interpolation - just replace m to n in quasi-quoter name:

[qm| foo {1+2} |] -- Result: "foo 3"
[qn| foo {1+2} |] -- Result: "foo {1+2}"

[qms| foo {1+2} |] -- Result: "foo 3"
[qns| foo {1+2} |] -- Result: "foo {1+2}"

[qmb| foo {1+2} |] -- Result: "foo 3"
[qnb| foo {1+2} |] -- Result: "foo {1+2}"

That's how you update some record inside interpolation block (you need to escape closing bracket):

{-# LANGUAGE QuasiQuotes #-}
import Text.InterpolatedString.QM (qm)
data Foo = Foo {bar :: Int, baz :: Int} deriving Show
main = let foo = Foo 10 20 in putStrLn [qm| Foo is: {foo {baz = 30\}} |]
-- Foo is: Foo {bar = 10, baz = 30}

Wanna make a contribution or maintain your own fork?

You can find some info for developers on wiki pages.


Viacheslav Lotsmanov


The Unlicense