bitvec
Spaceefficient bit vectors https://github.com/Bodigrim/bitvec
LTS Haskell 14.1:  1.0.0.1@rev:1 
Stackage Nightly 20190820:  1.0.0.1@rev:1 
Latest on Hackage:  1.0.0.1@rev:1 
Module documentation for 1.0.0.1
bitvec1.0.0.1@sha256:04ba2e92d4a6a4c1752047681bcdd8acb101fec7d80e8cde9d83506122611061,3760
bitvec
A newtype over Bool
with a better Vector
instance.
The vector
package represents unboxed arrays of Bool
spending 1 byte (8 bits) per boolean.
This library provides a newtype wrapper Bit
and a custom instance
of unboxed Vector
, which packs bits densely,
achieving 8x less memory footprint.
The performance stays mostly the same;
the most significant degradation happens for random writes
(up to 10% slower).
On the other hand, for certain bulk bit operations
Vector Bit
is up to 64x faster than Vector Bool
.
Thread safety
Data.Bit
is faster, but writes and flips are threadunsafe. This is because naive updates are not atomic: read the whole word from memory, then modify a bit, then write the whole word back.Data.Bit.ThreadSafe
is slower (up to 20%), but writes and flips are threadsafe.
Similar packages

array
is memoryefficient forBool
, but lacks a handyVector
interface and is not threadsafe.
Quick start
Consider the following (very naive) implementation of
the sieve of Eratosthenes. It returns a vector with True
at prime indices and False
at composite indices.
import Control.Monad
import Control.Monad.ST
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as MU
eratosthenes :: U.Vector Bool
eratosthenes = runST $ do
let len = 100
sieve < MU.replicate len True
MU.write sieve 0 False
MU.write sieve 1 False
forM_ [2 .. floor (sqrt (fromIntegral len))] $ \p > do
isPrime < MU.read sieve p
when isPrime $
forM_ [2 * p, 3 * p .. len  1] $ \i >
MU.write sieve i False
U.unsafeFreeze sieve
We can switch from Bool
to Bit
just by adding newtype constructors:
import Data.Bit
import Control.Monad
import Control.Monad.ST
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as MU
eratosthenes :: U.Vector Bit
eratosthenes = runST $ do
let len = 100
sieve < MU.replicate len (Bit True)
MU.write sieve 0 (Bit False)
MU.write sieve 1 (Bit False)
forM_ [2 .. floor (sqrt (fromIntegral len))] $ \p > do
Bit isPrime < MU.read sieve p
when isPrime $
forM_ [2 * p, 3 * p .. len  1] $ \i >
MU.write sieve i (Bit False)
U.unsafeFreeze sieve
Bit
based implementation requires 8x less memory to store
the vector. For large sizes it allows to crunch more data in RAM
without swapping. For smaller arrays it helps to fit into
CPU caches.
> listBits eratosthenes
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
There are several highlevel helpers, digesting bits in bulk,
which makes them up to 64x faster than respective counterparts
for Vector Bool
. One can query population count (popcount)
of a vector (giving us the primecounting function):
> countBits eratosthenes
25
And viceversa, query an address of the nth set bit (which corresponds to the nth prime number here):
> nthBitIndex (Bit True) 10 eratosthenes
Just 29
One may notice that the order of the inner traversal by i
does not matter and get tempted to run it in several parallel threads.
In this case it is vital to switch from Data.Bit
to Data.Bit.ThreadSafe
,
because the former is threadunsafe with regards to writes.
There is a moderate performance penalty (up to 20%)
for using the threadsafe interface.
Changes
1.0.0.1
 Performance improvements.
1.0.0.0
 Redesign API from the scratch.
 Add a threadsafe implementation.
 Add ‘nthBitIndex’ function.
0.2.0.1
 Fix ‘Read’ instance.
0.2.0.0
 Remove handwritten ‘Num’, ‘Real’, ‘Integral’, ‘Bits’ instances.
 Derive ‘Bits’ and ‘FiniteBits’ instances.
 Expose ‘Bit’ constructor directly and remove ‘fromBool’ function.
 Rename ‘toBool’ to ‘unBit’.
0.1.1.0
 Fix bugs in
MVector
andVector
instances ofBit
.  Speed up
MVector
andVector
instances ofBit
.