The vector library has an API similar to the famous Haskell list library, with many of the same names. for unboxed arrays. The library needs to be imported qualified as it shares the same function names as list operations in the Prelude. Just as with lists, you can iterate (map) over arrays, reduce them (fold), filter them, or join them in various ways: The vector package provides several types of array. The most general interface is via Data.Vector, which provides for boxed arrays, holding any type. which provide unboxed arrays (i.e. no closures) and storable arrays (data that is pinned, and may be passed to and from C via a Ptr). The vector package provides several array types, with an identical interface. They have different flexibility with respect to the types of values that may be stored in them, and different performance characteristics. The most flexible type is Data.Vector.Vector, which provides *boxed* arrays: arrays of pointers to Haskell values. These arrays are suitable for storing complex Haskell types (sum types, or algebraic data types), but a better choice for simple data types is Data.Vector.Unboxed. Simple, atomic types, and pair types can be stored in a more efficient manner: consecutive memory slots without pointers. The Data.Vector.Unboxed.Vector type provides unboxed arrays of types that are members of the Unbox class, including: Unboxed arrays should be preferred when you have unboxable elements, as they are generally more efficient. These arrays are pinned, and may be converted to and from pointers, that may be passed to C functions, using a number of functions: You can store new data types in Storable vectors, beyond those for instances that already exist, by writing a Storable instance for the type. Arrays can be created and operated on in a mutable fashion — using destructive updates, as in an imperative language. Once all operations are complete, the mutable array can be ‘frozen’ to a pure array, which changes its type. Mutable arrays plus freezing are quite useful for initializing arrays from data in the outside world. Here we use the Data.Vector.Generic.Mutable.new to allocate a new, uninitialized array, which we then write elements to (using unsafeWrite). By using the generic interface, we can construct boxed, storable or unboxed arrays all from the same code. The enumFrom*N functions are guaranteed to optimize well for any type. The enumFromTo functions might fall back to generating from lists if there is no specialization for your type. They are currently specialized to most Int/Word/Double/Float generators. As for almost all vector functions, if an enumerator is composed with a traversal or fold, they will fuse into a single loop. For example, we can fuse generation of an array of doubles, with computing the product of the square roots. The source program consists of two loops: Doubling the performance, by halving the number of traversals. Fusion also means we can avoid any intermediate data structure allocation. We often want to populate a vector using a external data file. The easiest way to do this is with bytestring IO, and Data.Vector.unfoldr (or the equivalent functions in Data.Vector.Unboxed or Data.Vector.Storable: The simplest way to parse a file of Int or Integer types is with a strict or lazy ByteString, and the readInt or readInteger functions: To load a file of floating point values into a vector, you can use bytestrings and the bytestring-lexing package, which provides readDouble and readFloat functions. An example: parsing a list of integers in text form, serializing them back in binary form, then loading that binary file: If we can parse from a file, we can also fill a vector with random numbers. We’ll use the mersenne-random package: We can also just use the vector-random package to generate new vectors initialized with the mersenne twister generator: We might want to fill a vector with a monadic action, and have a pure vector at the end. The Vector API now contains a standard replicateM for this purpose, but if your monadic action is in IO, the following code is more efficient: slice takes 3 arguments: the initial index to slice from, the number of elements to slice, and the vector to operate on. For performance reasons you may wish to avoid bounds checks, when you can prove that the substring or index will be in bounds. For these cases there are unsafe operations, that let you skip the bounds check: Source.