HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Setup

0 declarations
Example6 expressions
:m:set -XFlexibleContexts:set -XMagicHashimport Data.Function ((&))import Data.Functor.Identity (Identity(..))import System.IO.Unsafe (unsafePerformIO)
Example2 expressions
import Streamly.Data.Array (Array)import Streamly.Data.Stream (Stream)
Example5 expressions
import qualified Streamly.Data.Array as Arrayimport qualified Streamly.Data.Fold as Foldimport qualified Streamly.Data.ParserK as ParserKimport qualified Streamly.Data.Stream as Streamimport qualified Streamly.Data.StreamK as StreamK

For APIs that have not been released yet.

Example2 expressions
import qualified Streamly.Internal.Data.Array as Arrayimport qualified Streamly.Internal.Data.Stream as Stream

Design Notes

0 declarations

To summarize:

  • Arrays are finite and fixed in size

  • provide O(1) access to elements

  • store only data and not functions

  • provide efficient IO interfacing

Foldable instance is not provided because the implementation would be much less efficient compared to folding via streams. Semigroup and Monoid instances should be used with care; concatenating arrays using binary operations can be highly inefficient. Instead, use Streamly.Internal.Data.Stream.Chunked.toArray to concatenate N arrays at once.

Each array is one pointer visible to the GC. Too many small arrays (e.g. single byte) are only as good as holding those elements in a Haskell list. However, small arrays can be compacted into large ones to reduce the overhead. To hold 32GB memory in 32k sized buffers we need 1 million arrays if we use one array for each chunk. This is still significant to add pressure to GC.

The Array Type

85 declarations
valueconcat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
#

Convert a stream of arrays into a stream of their elements.

Example1 expression
concat = Stream.unfoldMany Array.reader
datadata Array a
#
Instances20IsList, Eq, Ord, Read, Show, IsString, …
  • Unbox a => IsList (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Int16)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Int32)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Int64)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Int8)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Word16)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Word32)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Word64)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Word8)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Char)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Eq (Array Int)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • (Unbox a, Eq a) => Eq (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type

    If the type allows a byte-by-byte comparison this instance can be overlapped by a more specific instance that uses byteCmp. Byte comparison can be significantly faster.

  • (Unbox a, Ord a) => Ord (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • (Unbox a, Read a, Show a) => Read (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • (Show a, Unbox a) => Show (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • a ~ Char => IsString (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Unbox a => Semigroup (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type

    This should not be used for combining many or N arrays as it would copy the two arrays everytime to a new array. For coalescing multiple arrays use fromChunksK instead.

  • Unbox a => Monoid (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
  • Serialize (Array a)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Serialize.Type
  • type Item (Array a) = aDefined in streamly-core-0.2.2 · Streamly.Internal.Data.Array.Type
valuelength :: Unbox a => Array a -> Int
#

O(1) Get the length of the array i.e. the number of elements in the array.

valuesplitAt :: Unbox a => Int -> Array a -> (Array a, Array a)
#

Create two slices of an array without copying the original array. The specified index i is the first index of the second slice.

valuenil :: Array a
#

Deprecated. Please use empty instead.

valueunsafeCreateOf :: (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
#

Like createOf but does not check the array bounds when writing. The fold driver must not call the step function more than n times otherwise it will corrupt the memory and crash. This function exists mainly because any conditional in the step function blocks fusion causing 10x performance slowdown.

valuecreate :: (MonadIO m, Unbox a) => Fold m a (Array a)
#

Fold the whole input to a single array.

Caution! Do not use this on infinite streams.

valuefromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
#

Create an Array from the first N elements of a stream. The array is allocated to size N, if the stream terminates before N elements then the array may hold less than N elements.

Example1 expression
fromStreamN n = Stream.fold (Array.writeN n)

Pre-release

valuefromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)
#

Create an Array from a stream. This is useful when we want to create a single array from a stream of unknown size. writeN is at least twice as efficient when the size is already known.

Example1 expression
fromStream = Stream.fold Array.write

Note that if the input stream is too large memory allocation for the array may fail. When the stream size is not known, chunksOf followed by processing of indvidual arrays in the resulting stream should be preferred.

Pre-release

valuefromPureStream :: Unbox a => Stream Identity a -> Array a
#

Convert a pure stream in Identity monad to an immutable array.

Same as the following but with better performance:

Example1 expression
fromPureStream = Array.fromList . runIdentity . Stream.toList
valuefromListN :: Unbox a => Int -> [a] -> Array a
#

Create an Array from the first N elements of a list. The array is allocated to size N, if the list terminates before N elements then the array may hold less than N elements.

valuechunksOf :: (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)
#

chunksOf n stream groups the elements in the input stream into arrays of n elements each.

Same as the following but may be more efficient:

Example1 expression
chunksOf n = Stream.foldMany (Array.writeN n)

Pre-release

valuefromByteStr# :: Addr# -> Array Word8
#

Copy a null terminated immutable Addr# Word8 sequence into an array.

Unsafe: The caller is responsible for safe addressing.

Note that this is completely safe when reading from Haskell string literals because they are guaranteed to be NULL terminated:

Example1 expression
Array.toList $ Array.fromByteStr# "\1\2\3\0"#[1,2,3]

Note that this should be evaluated strictly to ensure that we do not hold the reference to the pointer in a lazy thunk.

valuecompactGE
  1. :: (MonadIO m, Unbox a)
  2. => Int
  3. -> Stream m (Array a)
  4. -> Stream m (Array a)
#

compactGE n stream coalesces adjacent arrays in the stream until the size becomes greater than or equal to n.

Example1 expression
compactGE n = Stream.foldMany (Array.fCompactGE n)

Generates unpinned arrays irrespective of the pinning status of input arrays.

valueconcatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
#

Convert a stream of arrays into a stream of their elements reversing the contents of each array before flattening.

Example1 expression
concatRev = Stream.unfoldMany Array.readerRev
valuepin :: Array a -> IO (Array a)
#

Return a copy of the Array in pinned memory if unpinned, else return the original array.

valueunpin :: Array a -> IO (Array a)
#

Return a copy of the Array in unpinned memory if pinned, else return the original array.

valuefromPtrN :: Int -> Ptr Word8 -> Array Word8
#

Copy an immutable 'Ptr Word8' sequence into an array.

Unsafe: The caller is responsible for safe addressing.

Note that this should be evaluated strictly to ensure that we do not hold the reference to the pointer in a lazy thunk.

valuefromListRevN :: Unbox a => Int -> [a] -> Array a
#

Create an Array from the first N elements of a list in reverse order. The array is allocated to size N, if the list terminates before N elements then the array may hold less than N elements.

Pre-release

valuefromListRev :: Unbox a => [a] -> Array a
#

Create an Array from a list in reverse order. The list must be of finite size.

Pre-release

valuebyteCmp :: Array a -> Array a -> Ordering
#

Byte compare two arrays. Compare the length of the arrays. If the length is equal, compare the lexicographical ordering of two underlying byte arrays otherwise return the result of length comparison.

Unsafe: Note that the Unbox instance of sum types with constructors of different sizes may leave some memory uninitialized which can make byte comparison unreliable.

Pre-release

valuebyteEq :: Array a -> Array a -> Bool
#

Byte equality of two arrays.

Example1 expression
byteEq arr1 arr2 = (==) EQ $ Array.byteCmp arr1 arr2

Unsafe: See byteCmp.

valuesplice :: MonadIO m => Array a -> Array a -> m (Array a)
#

Copy two immutable arrays into a new array. If you want to splice more than two arrays then this operation would be highly inefficient because it would make a copy on every splice operation, instead use the fromChunksK operation to combine n immutable arrays.

valuefCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
#

Fold fCompactGE n coalesces adjacent arrays in the input stream until the size becomes greater than or equal to n.

Generates unpinned arrays irrespective of the pinning status of input arrays.

valuelCompactGE
  1. :: (MonadIO m, Unbox a)
  2. => Int
  3. -> Fold m (Array a) ()
  4. -> Fold m (Array a) ()
#

Like compactGE but for transforming folds instead of stream.

Example1 expression
lCompactGE n = Fold.many (Array.fCompactGE n)

Generates unpinned arrays irrespective of the pinning status of input arrays.

valueunsafeFreeze :: MutArray a -> Array a
#

Makes an immutable array using the underlying memory of the mutable array.

Please make sure that there are no other references to the mutable array lying around, so that it is never used after freezing it using unsafeFreeze. If the underlying array is mutated, the immutable promise is lost.

Pre-release

valueunsafeThaw :: Array a -> MutArray a
#

Makes a mutable array using the underlying memory of the immutable array.

Please make sure that there are no other references to the immutable array lying around, so that it is never used after thawing it using unsafeThaw. If the resulting array is mutated, any references to the older immutable array are mutated as well.

Pre-release

valueunsafeMakePure :: Monad m => Fold IO a b -> Fold m a b
#

Fold "step" has a dependency on "initial", and each step is dependent on the previous invocation of step due to state passing, finally extract depends on the result of step, therefore, as long as the fold is driven in the correct order the operations would be correctly ordered. We need to ensure that we strictly evaluate the previous step completely before the next step.

To not share the same array we need to make sure that the result of "initial" is not shared. Existential type ensures that it does not get shared across different folds. However, if we invoke "initial" multiple times for the same fold, there is a possiblity of sharing the two because the compiler would consider it as a pure value. One such example is the chunksOf combinator, or using an array creation fold with foldMany combinator. Is there a proper way in GHC to tell it to not share a pure expression in a particular case?

For this reason array creation folds have a MonadIO constraint. Pure folds could be unsafe and dangerous. This is dangerous especially when used with foldMany like operations.

Example1 expression
unsafePureWrite = Array.unsafeMakePure Array.write
valuefromByteStr :: Ptr Word8 -> Array Word8
#

Note that this should be evaluated strictly to ensure that we do not hold the reference to the pointer in a lazy thunk.

valueunsafeIndexIO :: Unbox a => Int -> Array a -> IO a
#

Return element at the specified index without checking the bounds.

Unsafe because it does not check the bounds of the array.

valuereaderUnsafe :: (Monad m, Unbox a) => Unfold m (Array a) a
#

Unfold an array into a stream, does not check the end of the array, the user is responsible for terminating the stream within the array bounds. For high performance application where the end condition can be determined by a terminating fold.

Written in the hope that it may be faster than "read", however, in the case for which this was written, "read" proves to be faster even though the core generated with unsafeRead looks simpler.

Pre-release

Construction

1 declaration

Random Access

5 declarations
valuelast :: Unbox a => Array a -> Maybe a
#
Example1 expression
last arr = Array.getIndexRev arr 0

Pre-release

valueindexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
#

Given a stream of array indices, read the elements on those indices from the supplied Array. An exception is thrown if an index is out of bounds.

This is the most general operation. We can implement other operations in terms of this:

read =
     let u = lmap (arr -> (0, length arr - 1)) Unfold.enumerateFromTo
      in Unfold.lmap f (indexReader arr)

readRev =
     let i = length arr - 1
      in Unfold.lmap f (indexReaderFromThenTo i (i - 1) 0)

Pre-release

valueindexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
#

Unfolds (from, then, to, array) generating a finite stream whose first element is the array value from the index from and the successive elements are from the indices in increments of then up to to. Index enumeration can occur downwards or upwards depending on whether then comes before or after from.

getIndicesFromThenTo =
    let f (from, next, to, arr) =
            (Stream.enumerateFromThenTo from next to, arr)
     in Unfold.lmap f getIndices

Unimplemented

Size

1 declaration
valuenull :: Array a -> Bool
#
Example1 expression
null arr = Array.byteLength arr == 0

Pre-release

Search

3 declarations
valuebinarySearch :: a -> Array a -> Maybe Int
#

Given a sorted array, perform a binary search to find the given element. Returns the index of the element if found.

Unimplemented

Casting

4 declarations
valuecast :: Unbox b => Array a -> Maybe (Array b)
#

Cast an array having elements of type a into an array having elements of type b. The length of the array should be a multiple of the size of the target element otherwise Nothing is returned.

valuecastUnsafe :: Array a -> Array b
#

Cast an array having elements of type a into an array having elements of type b. The array size must be a multiple of the size of type b otherwise accessing the last element of the array may result into a crash or a random value.

Pre-release

valueasCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
#

Convert an array of any type into a null terminated CString Ptr. If the array is unpinned it is first converted to a pinned array which requires a copy.

Unsafe

O(n) Time: (creates a copy of the array)

Pre-release

Subarrays

4 declarations
valuegetSliceUnsafe
  1. :: Unbox a
  2. => Int

    starting index

  3. -> Int

    length of the slice

  4. -> Array a
  5. -> Array a
#

O(1) Slice an array in constant time.

Caution: The bounds of the slice are not checked.

Unsafe

Pre-release

valueslicerFromLen
  1. :: (Monad m, Unbox a)
  2. => Int

    from index

  3. -> Int

    length of the slice

  4. -> Unfold m (Array a) (Array a)
#

Generate a stream of slices of specified length from an array, starting from the supplied array index. The last slice may be shorter than the requested length.

Pre-release/

valuesplitOn
  1. :: (Monad m, Unbox a)
  2. => a -> Bool
  3. -> Array a
  4. -> Stream m (Array a)
#

Split the array into a stream of slices using a predicate. The element matching the predicate is dropped.

Pre-release

Streaming Operations

1 declaration

Folding

2 declarations

Stream of Arrays

11 declarations
valueinterpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
#

Insert the given element between arrays and flatten.

Example1 expression
interpose x = Stream.interpose x Array.reader
valueinterposeSuffix
  1. :: (Monad m, Unbox a)
  2. => a
  3. -> Stream m (Array a)
  4. -> Stream m a
#

Insert the given element after each array and flatten. This is similar to unlines.

Example1 expression
interposeSuffix x = Stream.interposeSuffix x Array.reader
valuecompactLE
  1. :: (MonadIO m, Unbox a)
  2. => Int
  3. -> Stream m (Array a)
  4. -> Stream m (Array a)
#

compactLE n coalesces adjacent arrays in the input stream only if the combined size would be less than or equal to n.

Generates unpinned arrays irrespective of the pinning status of input arrays.

valuefoldChunks
  1. :: (MonadIO m, Unbox a)
  2. => Fold m a b
  3. -> Stream m (Array a)
  4. -> m b
#

Fold a stream of arrays using a Fold. This is equivalent to the following:

Example1 expression
foldChunks f = Stream.fold f . Stream.unfoldMany Array.reader
valuefoldBreakChunksK
  1. :: (MonadIO m, Unbox a)
  2. => Fold m a b
  3. -> StreamK m (Array a)
  4. -> m (b, StreamK m (Array a))
#

Fold a stream of arrays using a Fold and return the remaining stream.

The following alternative to this function allows composing the fold using the parser Monad:

foldBreakStreamK f s =
      fmap (first (fromRight undefined))
    $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s

We can compare perf and remove this one or define it in terms of that.

valueparseBreakChunksK
  1. :: (MonadIO m, Unbox a)
  2. => Parser a m b
  3. -> StreamK m (Array a)
  4. -> m (Either ParseError b, StreamK m (Array a))
#

Parse an array stream using the supplied Parser. Returns the parse result and the unconsumed stream. Throws ParseError if the parse fails.

The following alternative to this function allows composing the parser using the parser Monad:

Example1 expression
parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)

We can compare perf and remove this one or define it in terms of that.

Internal

Serialization

4 declarations
valueserialize :: Serialize a => a -> Array Word8
#

Properties: 1. Identity: deserialize . serialize == id 2. Encoded equivalence: serialize a == serialize a

valuepinnedSerialize :: Serialize a => a -> Array Word8
#

Serialize a Haskell type to a pinned byte array. The array is allocated using pinned memory so that it can be used directly in OS APIs for writing to file or sending over the network.

Properties: 1. Identity: deserialize . pinnedSerialize == id 2. Encoded equivalence: pinnedSerialize a == pinnedSerialize a

Deprecated

3 declarations