-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Builder for Text based on linear types -- -- Strict Text builder, which hides mutable buffer behind linear types -- and takes amortized linear time. @package text-builder-linear @version 0.1 -- | Low-level routines for Buffer manipulations. module Data.Text.Builder.Linear.Core -- | Internally Buffer is a mutable buffer. If a client gets hold of -- a variable of type Buffer, they'd be able to pass a mutable -- buffer to concurrent threads. That's why API below is carefully -- designed to prevent such possibility: clients always work with linear -- functions Buffer ⊸ Buffer instead and run them on an -- empty Buffer to extract results. -- -- In terms of linear-base Buffer is Consumable -- (see consumeBuffer) and Dupable (see -- dupBuffer), but not Movable. -- --
-- >>> :set -XOverloadedStrings -XLinearTypes -- -- >>> import Data.Text.Builder.Linear.Buffer -- -- >>> runBuffer (\b -> '!' .<| "foo" <| (b |> "bar" |>. '.')) -- "!foobar." ---- -- Remember: this is a strict builder, so on contrary to -- Data.Text.Lazy.Builder for optimal performance you should use -- strict left folds instead of lazy right ones. -- -- Starting from GHC 9.2, Buffer is an unlifted datatype, so you -- can put it into an unboxed tuple (# ..., ... #), but not into -- (..., ...). data Buffer ∷ TYPE ('BoxedRep 'Unlifted) -- | Run a linear function on an empty Buffer, producing -- Text. -- -- Be careful to write runBuffer (b -> ...) instead of -- runBuffer $ b -> ..., because current implementation of -- linear types lacks special support for ($). Another option is -- to enable {-# LANGUAGE BlockArguments #-} and write -- runBuffer b -> .... Alternatively, you can import -- Prelude.Linear.($) from linear-base. -- -- runBuffer is similar in spirit to mutable arrays API in -- Data.Array.Mutable.Linear, which provides functions like -- fromList ∷ [a] → (Vector a ⊸ -- Ur b) ⊸ Ur b. Here the initial buffer is -- always empty and b is Text. Since Text is -- Movable, Text and Ur Text are -- equivalent. runBuffer :: (Buffer ⊸ Buffer) ⊸ Text -- | Duplicate builder. Feel free to process results in parallel threads. -- Similar to Data.Unrestricted.Linear.Dupable from -- linear-base. -- -- It is a bit tricky to use because of current limitations of -- linear types with regards to let and where. E. g., -- one cannot write -- --
-- let (# b1, b2 #) = dupBuffer b in ("foo" <| b1) >< (b2 |> "bar")
--
--
-- Instead write:
--
--
-- >>> :set -XOverloadedStrings -XLinearTypes -XUnboxedTuples
--
-- >>> import Data.Text.Builder.Linear.Buffer
--
-- >>> runBuffer (\b -> (\(# b1, b2 #) -> ("foo" <| b1) >< (b2 |> "bar")) (dupBuffer b))
-- "foobar"
--
--
-- Note the unboxed tuple: starting from GHC 9.2, Buffer is an
-- unlifted datatype, so it cannot be put into (..., ...).
dupBuffer :: Buffer ⊸ (# Buffer, Buffer #)
-- | Consume buffer linearly, similar to
-- Data.Unrestricted.Linear.Consumable from
-- linear-base.
consumeBuffer :: Buffer ⊸ ()
-- | Erase buffer's content, replacing it with an empty Text.
eraseBuffer :: Buffer ⊸ Buffer
-- | Return buffer's size in bytes (not in Chars). This could
-- be useful to implement a lazy builder atop of a strict one.
byteSizeOfBuffer :: Buffer ⊸ (# Buffer, Word #)
-- | Return buffer's length in Chars (not in bytes). This could be
-- useful to implement dropEndBuffer and takeEndBuffer,
-- e. g.,
--
-- -- import Data.Unrestricted.Linear -- -- dropEndBuffer :: Word -> Buffer %1 -> Buffer -- dropEndBuffer n buf = -- ((# buf', len #) -> case move len of Ur len' -> takeBuffer (len' - n) buf') -- (lengthOfBuffer buf) --lengthOfBuffer :: Buffer ⊸ (# Buffer, Word #) -- | Slice Buffer by dropping given number of Chars. dropBuffer :: Word → Buffer ⊸ Buffer -- | Slice Buffer by taking given number of Chars. takeBuffer :: Word → Buffer ⊸ Buffer -- | Low-level routine to append data of unknown size to a Buffer. appendBounded :: Int → (∀ s. MArray s → Int → ST s Int) → Buffer ⊸ Buffer -- | Low-level routine to append data of known size to a Buffer. appendExact :: Int → (∀ s. MArray s → Int → ST s ()) → Buffer ⊸ Buffer -- | Low-level routine to prepend data of unknown size to a Buffer. prependBounded :: Int → (∀ s. MArray s → Int → ST s Int) → (∀ s. MArray s → Int → ST s Int) → Buffer ⊸ Buffer -- | Low-level routine to append data of unknown size to a Buffer. prependExact :: Int → (∀ s. MArray s → Int → ST s ()) → Buffer ⊸ Buffer -- | Concatenate two Buffers, potentially mutating both of them. -- -- You likely need to use dupBuffer to get hold on two builders at -- once: -- --
-- >>> :set -XOverloadedStrings -XLinearTypes -XUnboxedTuples
--
-- >>> import Data.Text.Builder.Linear.Buffer
--
-- >>> runBuffer (\b -> (\(# b1, b2 #) -> ("foo" <| b1) >< (b2 |> "bar")) (dupBuffer b))
-- "foobar"
--
(><) :: Buffer ⊸ Buffer ⊸ Buffer
infix 6 ><
-- | Buffer for strict Text, based on linear types.
module Data.Text.Builder.Linear.Buffer
-- | Internally Buffer is a mutable buffer. If a client gets hold of
-- a variable of type Buffer, they'd be able to pass a mutable
-- buffer to concurrent threads. That's why API below is carefully
-- designed to prevent such possibility: clients always work with linear
-- functions Buffer ⊸ Buffer instead and run them on an
-- empty Buffer to extract results.
--
-- In terms of linear-base Buffer is Consumable
-- (see consumeBuffer) and Dupable (see
-- dupBuffer), but not Movable.
--
-- -- >>> :set -XOverloadedStrings -XLinearTypes -- -- >>> import Data.Text.Builder.Linear.Buffer -- -- >>> runBuffer (\b -> '!' .<| "foo" <| (b |> "bar" |>. '.')) -- "!foobar." ---- -- Remember: this is a strict builder, so on contrary to -- Data.Text.Lazy.Builder for optimal performance you should use -- strict left folds instead of lazy right ones. -- -- Starting from GHC 9.2, Buffer is an unlifted datatype, so you -- can put it into an unboxed tuple (# ..., ... #), but not into -- (..., ...). data Buffer ∷ TYPE ('BoxedRep 'Unlifted) -- | Run a linear function on an empty Buffer, producing -- Text. -- -- Be careful to write runBuffer (b -> ...) instead of -- runBuffer $ b -> ..., because current implementation of -- linear types lacks special support for ($). Another option is -- to enable {-# LANGUAGE BlockArguments #-} and write -- runBuffer b -> .... Alternatively, you can import -- Prelude.Linear.($) from linear-base. -- -- runBuffer is similar in spirit to mutable arrays API in -- Data.Array.Mutable.Linear, which provides functions like -- fromList ∷ [a] → (Vector a ⊸ -- Ur b) ⊸ Ur b. Here the initial buffer is -- always empty and b is Text. Since Text is -- Movable, Text and Ur Text are -- equivalent. runBuffer :: (Buffer ⊸ Buffer) ⊸ Text -- | Duplicate builder. Feel free to process results in parallel threads. -- Similar to Data.Unrestricted.Linear.Dupable from -- linear-base. -- -- It is a bit tricky to use because of current limitations of -- linear types with regards to let and where. E. g., -- one cannot write -- --
-- let (# b1, b2 #) = dupBuffer b in ("foo" <| b1) >< (b2 |> "bar")
--
--
-- Instead write:
--
--
-- >>> :set -XOverloadedStrings -XLinearTypes -XUnboxedTuples
--
-- >>> import Data.Text.Builder.Linear.Buffer
--
-- >>> runBuffer (\b -> (\(# b1, b2 #) -> ("foo" <| b1) >< (b2 |> "bar")) (dupBuffer b))
-- "foobar"
--
--
-- Note the unboxed tuple: starting from GHC 9.2, Buffer is an
-- unlifted datatype, so it cannot be put into (..., ...).
dupBuffer :: Buffer ⊸ (# Buffer, Buffer #)
-- | Consume buffer linearly, similar to
-- Data.Unrestricted.Linear.Consumable from
-- linear-base.
consumeBuffer :: Buffer ⊸ ()
-- | Erase buffer's content, replacing it with an empty Text.
eraseBuffer :: Buffer ⊸ Buffer
-- | This is just a normal foldl', but with a linear arrow and
-- potentially unlifted accumulator.
foldlIntoBuffer :: (Buffer ⊸ a → Buffer) → Buffer ⊸ [a] → Buffer
-- | Append Text suffix to a Buffer by mutating it. If a
-- suffix is statically known, consider using (|>#) for optimal
-- performance.
--
-- -- >>> :set -XOverloadedStrings -XLinearTypes -- -- >>> runBuffer (\b -> b |> "foo" |> "bar") -- "foobar" --(|>) :: Buffer ⊸ Text → Buffer infixl 6 |> -- | Append Char to a Buffer by mutating it. -- --
-- >>> :set -XLinearTypes -- -- >>> runBuffer (\b -> b |>. 'q' |>. 'w') -- "qw" --(|>.) :: Buffer ⊸ Char → Buffer infixl 6 |>. -- | Append a null-terminated UTF-8 string to a Buffer by mutating -- it. E. g., -- --
-- >>> :set -XOverloadedStrings -XLinearTypes -XMagicHash -- -- >>> runBuffer (\b -> b |># "foo"# |># "bar"#) -- "foobar" ---- -- The literal string must not contain zero bytes \0, this -- condition is not checked. -- -- Note the inconsistency in naming: unfortunately, GHC parser does not -- allow for #<|. (|>#) :: Buffer ⊸ Addr# → Buffer infixl 6 |># -- | Prepend Text prefix to a Buffer by mutating it. If a -- prefix is statically known, consider using (<|#) for optimal -- performance. -- --
-- >>> :set -XOverloadedStrings -XLinearTypes -- -- >>> runBuffer (\b -> "foo" <| "bar" <| b) -- "foobar" --(<|) :: Text → Buffer ⊸ Buffer infixr 6 <| -- | Prepend Char to a Buffer by mutating it. -- --
-- >>> :set -XLinearTypes -- -- >>> runBuffer (\b -> 'q' .<| 'w' .<| b) -- "qw" --(.<|) :: Char → Buffer ⊸ Buffer infixr 6 .<| -- | Prepend a null-terminated UTF-8 string to a Buffer by mutating -- it. E. g., -- --
-- >>> :set -XOverloadedStrings -XLinearTypes -XMagicHash -- -- >>> runBuffer (\b -> "foo"# <|# "bar"# <|# b) -- "foobar" ---- -- The literal string must not contain zero bytes \0, this -- condition is not checked. (<|#) :: Addr# → Buffer ⊸ Buffer infixr 6 <|# -- | Concatenate two Buffers, potentially mutating both of them. -- -- You likely need to use dupBuffer to get hold on two builders at -- once: -- --
-- >>> :set -XOverloadedStrings -XLinearTypes -XUnboxedTuples
--
-- >>> import Data.Text.Builder.Linear.Buffer
--
-- >>> runBuffer (\b -> (\(# b1, b2 #) -> ("foo" <| b1) >< (b2 |> "bar")) (dupBuffer b))
-- "foobar"
--
(><) :: Buffer ⊸ Buffer ⊸ Buffer
infix 6 ><
-- | Append decimal number.
(|>$) :: (Integral a, FiniteBits a) ⇒ Buffer ⊸ a → Buffer
infixl 6 |>$
-- | Prepend decimal number.
($<|) :: (Integral a, FiniteBits a) ⇒ a → Buffer ⊸ Buffer
infixr 6 $<|
-- | Append double.
(|>%) :: Buffer ⊸ Double → Buffer
infixl 6 |>%
-- | Prepend double
(%<|) :: Double → Buffer ⊸ Buffer
infixr 6 %<|
-- | Append hexadecimal number.
(|>&) :: (Integral a, FiniteBits a) ⇒ Buffer ⊸ a → Buffer
infixl 6 |>&
-- | Prepend hexadecimal number.
(&<|) :: (Integral a, FiniteBits a) ⇒ a → Buffer ⊸ Buffer
infixr 6 &<|
-- | Append given number of spaces.
(|>…) :: Buffer ⊸ Word → Buffer
infixr 6 |>…
-- | Prepend given number of spaces.
(…<|) :: Word → Buffer ⊸ Buffer
infixr 6 …<|
-- | Builder for strict Text, based on linear types. It's
-- consistently outperforms Data.Text.Lazy.Builder from
-- text as well as a strict builder from text-builder,
-- and scales better.
module Data.Text.Builder.Linear
-- | Thin wrapper over Buffer with a handy Semigroup
-- instance.
--
-- -- >>> :set -XOverloadedStrings -XMagicHash -- -- >>> fromText "foo" <> fromChar '_' <> fromAddr "bar"# -- "foo_bar" ---- -- Remember: this is a strict builder, so on contrary to -- Data.Text.Lazy.Builder for optimal performance you should use -- strict left folds instead of lazy right ones. -- -- Note that (similar to other builders) concatenation of Builders -- allocates thunks. This is to a certain extent mitigated by aggressive -- inlining, but it is faster to use Buffer directly. newtype Builder Builder :: (Buffer ⊸ Buffer) → Builder [unBuilder] :: Builder → Buffer ⊸ Buffer -- | Run Builder computation on an empty Buffer, returning -- Text. -- --
-- >>> :set -XOverloadedStrings -XMagicHash -- -- >>> runBuilder (fromText "foo" <> fromChar '_' <> fromAddr "bar"#) -- "foo_bar" ---- -- This function has a polymorphic arrow and thus can be used both in -- usual and linear contexts. runBuilder :: ∀ m. Builder %m → Text -- | Create Builder, containing a given Text. -- --
-- >>> :set -XOverloadedStrings -- -- >>> fromText "foo" <> fromText "bar" -- "foobar" --fromText :: Text → Builder -- | Create Builder, containing a given Char. -- --
-- >>> fromChar 'x' <> fromChar 'y' -- "xy" --fromChar :: Char → Builder -- | Create Builder, containing a null-terminated UTF-8 string, -- specified by Addr#. -- --
-- >>> :set -XMagicHash -- -- >>> fromAddr "foo"# <> fromAddr "bar"# -- "foobar" --fromAddr :: Addr# → Builder -- | Create Builder, containing decimal representation of a given -- number. -- --
-- >>> fromChar 'x' <> fromDec (123 :: Int) -- "x123" --fromDec :: (Integral a, FiniteBits a) ⇒ a → Builder -- | Create Builder, containing hexadecimal representation of a -- given number. -- --
-- >>> :set -XMagicHash -- -- >>> fromAddr "0x"# <> fromHex (0x123def :: Int) -- "0x123def" --fromHex :: (Integral a, FiniteBits a) ⇒ a → Builder -- | Create Builder, containing a given Double. -- --
-- >>> :set -XMagicHash -- -- >>> fromAddr "pi="# <> fromDouble pi -- "pi=3.141592653589793" --fromDouble :: Double → Builder instance GHC.Show.Show Data.Text.Builder.Linear.Builder instance GHC.Base.Semigroup Data.Text.Builder.Linear.Builder instance GHC.Base.Monoid Data.Text.Builder.Linear.Builder instance Data.String.IsString Data.Text.Builder.Linear.Builder