prettyprinter-interp-0.1.0.0: src/Prettyprinter/Interpolate.hs
{-|
Copyright : (C) 2022 , Peter Lebbing
License : BSD2 (see the file LICENSE)
Maintainer : Peter Lebbing <peter@digitalbrains.com>
=Efficient interpolation for "Prettyprinter"
This module provides efficient interpolation of
[@string-interpolate@](https://hackage.haskell.org/package/string-interpolate)
quasi quoters when used with
[@prettyprinter@s](https://hackage.haskell.org/package/prettyprinter)
'Prettyprinter.Doc'uments.
The normal quasi quoters from @string-interpolate@ do work when used as a
@Doc@. Newlines are even converted to @Prettyprinter.@'Prettyprinter.line'.
However, this method is inefficient. The following code functions correctly:
@
{\-\# LANGUAGE OverloadedStrings \#-\}
{\-\# LANGUAGE QuasiQuotes \#-\}
module Main where
import Data.String.Interpolate
import Data.Text (Text)
import Prettyprinter
f :: 'Text'
f = "world"
g :: Doc ()
g = ['i'|Hello #{f}!|]
main :: IO ()
main = print g
@
However, what happens under the hood is that @f@ is converted to 'String', the
interpolated string is built manipulating @String@s, and then the output is
converted to 'Data.Text.Text' in
[@prettyprinter@](https://hackage.haskell.org/package/prettyprinter). The
following code is much better:
@
g = 'pretty' ([i|Hello #{f}!|] :: Text)
@
Now, the interpolated string is constructed as @Text@, and this is passed
cleanly into @Doc@ which also uses @Text@ as its underlying type for
representation of text. At no point is @f@ converted to @String@, and the
string construction benefits from the performance of @Text@. And again,
newlines are converted to 'Prettyprinter.line'.
This module defines wrapper quasi quoters that automatically perform the
@pretty@ invocation, and can simply be used as:
@
g = ['di'|Hello #{f}!|]
@
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
module Prettyprinter.Interpolate
( di
, __di
, diii
, d__i'E
, d__i'L
, diii'E
, diii'L
) where
#if MIN_VERSION_prettyprinter(1,7,0)
import Prettyprinter (Pretty(pretty))
#else
import Data.Text.Prettyprint.Doc (Pretty(pretty))
#endif
import Data.String.Interpolate
import Data.Text (Text)
import Language.Haskell.TH (Q)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Haskell.TH.Syntax (Name)
wrapper :: Name -> QuasiQuoter -> QuasiQuoter
wrapper nm wrapped = QuasiQuoter
{ quoteExp = \s -> [| pretty ($(quoteExp wrapped s) :: Text) |]
, quotePat = const $ errQQType nm "pattern"
, quoteType = const $ errQQType nm "type"
, quoteDec = const $ errQQType nm "declaration"
}
-- | Wrapper around the 'i' quasi quoter, producing a t'Prettyprinter.Doc'
--
-- Newlines in the text are converted to 'Prettyprinter.line'.
di :: QuasiQuoter
di = wrapper 'di i
-- | Wrapper around the '__i' quasi quoter, producing a t'Prettyprinter.Doc'
--
-- Newlines in the text are converted to 'Prettyprinter.line'.
__di :: QuasiQuoter
__di = wrapper '__di __i
-- | Wrapper around the 'iii' quasi quoter, producing a t'Prettyprinter.Doc'
--
-- Newlines in the text are converted to 'Prettyprinter.line'.
diii :: QuasiQuoter
diii = wrapper 'diii iii
-- | Wrapper around the '__i'E' quasi quoter, producing a t'Prettyprinter.Doc'
--
-- Newlines in the text are converted to 'Prettyprinter.line'.
d__i'E :: QuasiQuoter
d__i'E = wrapper 'd__i'E __i'E
-- | Wrapper around the '__i'L' quasi quoter, producing a t'Prettyprinter.Doc'
--
-- Newlines in the text are converted to 'Prettyprinter.line'.
d__i'L :: QuasiQuoter
d__i'L = wrapper 'd__i'L __i'L
-- | Wrapper around the 'iii'E' quasi quoter, producing a t'Prettyprinter.Doc'
--
-- Newlines in the text are converted to 'Prettyprinter.line'.
diii'E :: QuasiQuoter
diii'E = wrapper 'diii'E iii'E
-- | Wrapper around the 'iii'L' quasi quoter, producing a t'Prettyprinter.Doc'
--
-- Newlines in the text are converted to 'Prettyprinter.line'.
diii'L :: QuasiQuoter
diii'L = wrapper 'diii'L iii'L
errQQ :: Name -> String -> Q a
errQQ nm msg = fail $ show nm <> ": " <> msg
errQQType :: Name -> String -> Q a
errQQType nm ty = errQQ nm $ "This QuasiQuoter cannot be used as a " <> ty