HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Moduletext-builder-linear-0.1.2GHC2021

Data.Text.Builder.Linear.Core

Low-level routines for Buffer manipulations.

  • 1 type
  • 15 values

Type

1 declaration
datadata Buffer where
#

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.

Example3 expressions
:set -XOverloadedStrings -XLinearTypesimport Data.Text.Builder.Linear.BufferrunBuffer (\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.

Buffer is an unlifted datatype, so you can put it into an unboxed tuple (# ..., ... #), but not into (..., ...).

Basic interface

10 declarations
valuerunBuffer :: (Buffer %1 -> Buffer) %1 -> Text
#

Run a linear function on an empty Buffer, producing a strict 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 ($) 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.

valuedupBuffer :: Buffer %1 -> (# Buffer, Buffer #)
#

Duplicate builder. Feel free to process results in parallel threads. Similar to 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:

Example3 expressions
:set -XOverloadedStrings -XLinearTypes -XUnboxedTuplesimport Data.Text.Builder.Linear.BufferrunBuffer (\b -> case dupBuffer b of (# b1, b2 #) -> ("foo" <| b1) >< (b2 |> "bar"))"foobar"

Note the unboxed tuple: Buffer is an unlifted datatype, so it cannot be put into (..., ...).

valuelengthOfBuffer :: Buffer %1 -> (# 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 = case lengthOfBuffer buf of
  (# buf', len #) -> case move len of
    Ur len' -> takeBuffer (len' - n) buf'
valuenewEmptyBuffer :: Buffer %1 -> (# Buffer, Buffer #)
#

Create an empty Buffer.

The first Buffer is the input and the second is a new empty Buffer.

This function is needed in some situations, e.g. with justifyRight. The following example creates a utility function that justify a text and then append it to a buffer.

Example4 expressions
:set -XOverloadedStrings -XLinearTypes -XUnboxedTuplesimport Data.Text.Builder.Linear.Bufferimport Data.Text (Text):{appendJustified :: Buffer %1 -> Text -> BufferappendJustified b t = case newEmptyBuffer b of  -- Note that we need to create a new buffer from the text, in order  -- to justify only the text and not the input buffer.  (# b', empty #) -> b' >< justifyRight 12 ' ' (empty |> t):}
Example1 expression
runBuffer (\b -> (b |> "Test:") `appendJustified` "foo" `appendJustified` "bar")"Test:         foo         bar"

Note: a previous buffer is necessary in order to create an empty buffer with the same characteristics.

Text concatenation

5 declarations
valueappendBounded
  1. :: Int

    Upper bound for the number of bytes, written by an action

  2. -> (forall s. MArray s -> Int -> ST s Int)

    Action, which writes bytes starting from the given offset and returns an actual number of bytes written.

  3. -> Buffer
  4. -> Buffer
#

Low-level routine to append data of unknown size to a Buffer.

valueappendExact
  1. :: Int

    Exact number of bytes, written by an action

  2. -> (forall s. MArray s -> Int -> ST s ())

    Action, which writes bytes starting from the given offset

  3. -> Buffer
  4. -> Buffer
#

Low-level routine to append data of known size to a Buffer.

valueprependBounded
  1. :: Int

    Upper bound for the number of bytes, written by an action

  2. -> (forall s. MArray s -> Int -> ST s Int)

    Action, which writes bytes finishing before the given offset and returns an actual number of bytes written.

  3. -> (forall s. MArray s -> Int -> ST s Int)

    Action, which writes bytes starting from the given offset and returns an actual number of bytes written.

  4. -> Buffer
  5. -> Buffer
#

Low-level routine to prepend data of unknown size to a Buffer.

valueprependExact
  1. :: Int

    Exact number of bytes, written by an action

  2. -> (forall s. MArray s -> Int -> ST s ())

    Action, which writes bytes starting from the given offset

  3. -> Buffer
  4. -> Buffer
#

Low-level routine to append data of known size to a Buffer.

value(><) :: Buffer %1 -> Buffer %1 -> Buffer
#

Concatenate two Buffers, potentially mutating both of them.

You likely need to use dupBuffer to get hold on two builders at once:

Example3 expressions
:set -XOverloadedStrings -XLinearTypes -XUnboxedTuplesimport Data.Text.Builder.Linear.BufferrunBuffer (\b -> case dupBuffer b of (# b1, b2 #) -> ("foo" <| b1) >< (b2 |> "bar"))"foobar"