Skip to content

Commit f0a3db2

Browse files
committed
Apply the one-shot trick to Builder
See the new comment in response to https://gitlab.haskell.org/ghc/ghc/-/issues/23822.
1 parent e8a9b52 commit f0a3db2

File tree

1 file changed

+27
-1
lines changed

1 file changed

+27
-1
lines changed

Data/ByteString/Builder/Internal.hs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
{-# LANGUAGE ScopedTypeVariables, CPP, BangPatterns, RankNTypes, TupleSections #-}
1+
{-# LANGUAGE ScopedTypeVariables, CPP, BangPatterns, RankNTypes, TupleSections,
2+
PatternSynonyms #-}
23
{-# LANGUAGE Unsafe #-}
34
{-# OPTIONS_HADDOCK not-home #-}
45
-- | Copyright : (c) 2010 - 2011 Simon Meier
@@ -137,6 +138,7 @@ import qualified Data.ByteString.Internal.Type as S
137138
import qualified Data.ByteString.Lazy.Internal as L
138139
import qualified Data.ByteString.Short.Internal as Sh
139140

141+
import qualified GHC.Exts
140142
import qualified GHC.IO.Buffer as IO (Buffer(..), newByteBuffer)
141143
import GHC.IO.Handle.Internals (wantWritableHandle, flushWriteBuffer)
142144
import GHC.IO.Handle.Types (Handle__, haByteBuffer, haBufferMode)
@@ -326,7 +328,31 @@ fillWithBuildStep step fDone fFull fChunk !br = do
326328
-- They are 'Monoid's where
327329
-- 'mempty' is the zero-length sequence and
328330
-- 'mappend' is concatenation, which runs in /O(1)/.
331+
#if (MIN_VERSION_base(4,10,0))
332+
newtype Builder = Builder' (forall r. BuildStep r -> BuildStep r)
333+
pattern Builder :: (forall r. BuildStep r -> BuildStep r) -> Builder
334+
pattern Builder f <- Builder' f
335+
where
336+
-- We want to tell the compiler to eta-expand over the BufferRange of a
337+
-- BuildStep the same as it eta-expands over State# tokens.
338+
-- This is important for loops such as `foldMap (B.word8 . fromIntegral) xs`
339+
-- (see https://gitlab.haskell.org/ghc/ghc/-/issues/23822#note_520437)
340+
-- where otherwise the compiler thinks `empty bs` is worth sharing.
341+
-- The usual way to do that is via GHC.Exts.oneShot on `\br`.
342+
--
343+
-- By contrast, we refrain from marking the BuildStep argument as one-shot,
344+
-- because that could lead to undesirable duplication of work in an
345+
-- expression like
346+
--
347+
-- > let t = expensive 42 in stimes 1000 (Builder $ \bs br -> ... t ...)
348+
--
349+
-- Marking `\bs` one-shot as well tells the compiler that it's fine to float
350+
-- the definition of `t` inside the builder -- thus executing `expensive`
351+
-- 1000 times instead of just once.
352+
Builder f = Builder' (\bs -> GHC.Exts.oneShot $ \br -> f bs br)
353+
#else
329354
newtype Builder = Builder (forall r. BuildStep r -> BuildStep r)
355+
#endif
330356

331357
-- | Construct a 'Builder'. In contrast to 'BuildStep's, 'Builder's are
332358
-- referentially transparent.

0 commit comments

Comments
 (0)