packages feed

string-interpolate 0.3.3.0 → 0.3.4.0

raw patch · 7 files changed

+113/−16 lines, 7 filesdep ~QuickCheckdep ~haskell-src-extsdep ~quickcheck-instances

Dependency ranges changed: QuickCheck, haskell-src-exts, quickcheck-instances, template-haskell

Files

CHANGELOG.md view
@@ -2,6 +2,11 @@  ## Unreleased +## v0.3.4.0 (2024-07-09)+++ Fixed a performance bug causing exponential compilation times when using+  lots of interpolations (thanks mpickering!)+ ## v0.3.3.0 (2024-01-24)  + Added support for GHC 9.8
README.md view
@@ -308,3 +308,17 @@ Enable either the `text-builder` or `bytestring-builder` Cabal flag, depending on your need, and you should see speedups constructing large strings, at the cost of slowing down smaller outputs.++## Release policy++As of July 2024, `string-interpolate` is essentially "done;" it does its job+reliably, and no further enhancements or feature additions are planned. Going+forward, the expectation is that any further releases are mostly for performance+improvements, bugfixes, or necessary security patches.++If major API rework or breaking changes become planned in the future,+`string-interpolate` will maintain strict backwards compatibility (i.e. no code+changes should be necessary in any existing code consumers) for a period no less+than 3 years. Note that this only applies to the exports of the+`Data.String.Interpolate` module; all other modules should be considered+internal.
src/lib/Data/String/Interpolate.hs view
@@ -61,7 +61,6 @@  import Data.Foldable ( traverse_ ) import Data.List     ( intercalate )-import Data.Proxy  import qualified Language.Haskell.Exts.Extension as Ext import           Language.Haskell.Exts.Parser@@ -70,7 +69,7 @@ import           Language.Haskell.TH import           Language.Haskell.TH.Quote       ( QuasiQuoter(..) ) -import Data.String.Interpolate.Conversion ( build, finalize, interpolate, ofString )+import Data.String.Interpolate.Conversion ( proxyWrapper, build, finalize, interpolate, ofString )  import Data.String.Interpolate.Lines      ( IndentWarning(..), Mindent(..), handleIndents ) import Data.String.Interpolate.Parse@@ -116,16 +115,21 @@ -- Produce the final Template Haskell expression. Handles collapsing -- intermediate strings. outputToExp :: [OutputSegment] -> Q Exp-outputToExp segs = [|finalize Proxy $(go (collapseStrings segs))|]+outputToExp segs = finalExp   where-    go :: [OutputSegment] -> Q Exp-    go = foldr-      (\seg qexp -> [|build Proxy $(renderExp seg) $(qexp)|])-      [|ofString Proxy ""|]+    finalExp = [| proxyWrapper $ \proxy -> $(mkFinalize [| proxy |]) |] -    renderExp :: OutputSegment -> Q Exp-    renderExp (OfString str)     = [|ofString Proxy str|]-    renderExp (Interpolate expr) = [|interpolate Proxy $(reifyExpression expr)|]+    mkFinalize :: Q Exp -> Q Exp+    mkFinalize proxy = [|finalize $proxy $(go proxy (collapseStrings segs))|]++    go :: Q Exp -> [OutputSegment] -> Q Exp+    go proxy = foldr+      (\seg qexp -> [|build $proxy $(renderExp proxy seg) $(qexp)|])+      [|ofString $proxy ""|]++    renderExp :: Q Exp -> OutputSegment -> Q Exp+    renderExp proxy (OfString str)     = [|ofString $proxy str|]+    renderExp proxy (Interpolate expr) = [|interpolate $proxy $(reifyExpression expr)|]  type Interpolator = ParseOutput -> Q Lines 
src/lib/Data/String/Interpolate/Conversion.hs view
@@ -19,7 +19,7 @@  module Data.String.Interpolate.Conversion   ( IsCustomSink, InterpSink(..), Interpolatable(..)-  , bsToTextBuilder, lbsToTextBuilder, encodeCharUTF8+  , bsToTextBuilder, lbsToTextBuilder, encodeCharUTF8, proxyWrapper   ) where 
src/lib/Data/String/Interpolate/Conversion/Classes.hs view
@@ -2,10 +2,12 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies          #-} {-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeApplications      #-}  module Data.String.Interpolate.Conversion.Classes   ( B(..)-  , IsCustomSink, InterpSink(..), Interpolatable(..)+  , IsCustomSink, InterpSink(..), Interpolatable(..), proxyWrapper   ) where @@ -35,6 +37,13 @@   IsCustomSink LB.ByteString = 'True   IsCustomSink LB.Builder = 'True   IsCustomSink _ = 'False+++-- | Used to indicate to GHC that the value of all the proxy arguments is the same.+proxyWrapper :: forall final flag . (IsCustomSink final ~ flag) => (Proxy flag -> final) -> final+proxyWrapper k =+  let proxy = Proxy @(IsCustomSink final)+  in  k proxy  -- | Something that can be interpolated into. class IsCustomSink dst ~ flag => InterpSink (flag :: Bool) dst where
string-interpolate.cabal view
@@ -1,7 +1,7 @@ cabal-version: 1.18  name:           string-interpolate-version:        0.3.3.0+version:        0.3.4.0 synopsis:       Haskell string/text/bytestring interpolation that just works description:    Unicode-aware string interpolation that handles all textual types.                 .@@ -91,7 +91,7 @@     build-depends:         base ==4.*       , string-interpolate-      , QuickCheck <2.15+      , QuickCheck <2.16       , bytestring <0.13       , text <2.2       , template-haskell <2.22@@ -112,7 +112,7 @@     build-depends:         base ==4.*       , string-interpolate-      , QuickCheck <2.15+      , QuickCheck <2.16       , bytestring <0.13       , text <2.2       , deepseq <1.6
test/spec.hs view
@@ -18,7 +18,7 @@ import           Data.Char               ( chr, isSpace ) import           Data.Foldable           ( foldMap ) import qualified Data.HashMap.Strict     as HM-import           Data.List               ( sort )+import           Data.List               ( sort, intersperse ) import           Data.Semigroup import qualified Data.Text               as T import qualified Data.Text.Lazy          as LT@@ -75,6 +75,71 @@     it "should parse TypeApplications" $ do       let expected :: String = "2"       [i|#{show @Int 2}|] `shouldBe` expected++    -- This test is primarily a regression test against a performance issue+    -- caused by GHC needing to unify all the proxies passed in the generated+    -- code.+    -- See <https://gitlab.com/williamyaoh/string-interpolate/-/merge_requests/72>+    -- and <https://gitlab.haskell.org/ghc/ghc/-/issues/24984>+    -- It would be preferable if we had something that would actually *fail*+    -- if compilation took too long, but compilation of the test suite being+    -- slow should be enough of a smoke signal.+    it "should work with many interpolations" $+      let x = ()+          (expected :: String) = concat $ intersperse " : " (replicate 780 (show x))+      in [iii| #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x}+      |] `shouldBe` expected      context "when using String as a parameter" $ do       prop "just interpolating should be id" $