haskell / haskell/vector

Unboxed is not a good default unboxed vector

Open
#250 34 comments 1 reaction 0 assignees View on GitHub
Dominant language
Haskell
Stars
400
Forks
145
PR merge metrics
No merged PRs in 30d

Description

Currently the documentation (or lack of it) suggests to use `Unboxed` if you want unboxed, low-overhead vectors. In fact [the wiki linked in the description](https://wiki.haskell.org/Numeric_Haskell:_A_Vector_Tutorial#Array_Types) says:

> End users should use Data.Vector.Unboxed for most cases

However, in my experience, it's really not the better default, as opposed to `Storable` (or perhaps `Primitive`). Here I propose to update the documentation to make `Unboxed` less prominent, and even explicitly discourage its use (to counter the natural appeal of its name).

### The `Unbox` class is hard to understand

It involves

+ Multi-param type classes, and the class synonym trick (`Unbox`)
+ Data type families (which I think are even more niche than plain type families)
+ The names `MVector` and `Vector` are used both for classes and data type families, adding to the confusion
+ `PrimMonad` (should monads even be a prerequisite to use efficient vectors for user-defined types?)

Hence, most new users would probably be unable to complete the sketched implementation of `Unbox` for `Complex` in [the documentation of `Data.Vector.Unboxed`](https://hackage.haskell.org/package/vector-0.12.0.3/docs/Data-Vector-Unboxed.html).

Furthermore, because of the methods parameterized by `PrimMonad` instances, it's not possible to use `DerivingVia` to abstract any of those details. So you either [pay 30 lines of boilerplate](https://hackage.haskell.org/package/vector-0.12.0.3/docs/src/Data.Vector.Unboxed.Base.html#line-354), or use CPP/Template Haskell. Even assuming users are capable of adapting that boilerplate to their own types, that doesn't look "very easy" (quoted from the documentation).

### Unboxed is not the most efficient

In addition, although `Unboxed` is touted as efficient because vectors of tuples are merely tuples of vectors, a flat `Storable` vector is actually more efficient in almost all cases: it has much fewer indirections and having every entry in a contiguous piece of memory makes it more cache-friendly. The `Storable` class is also so much easier to understand and use than `Unbox`. Access patterns taking advantage of `Unboxed`'s layout are arguably rare.

That means this sentence at the top of `Data.Vector.Primitive` (which is similar to `Storable` in those respects) is incorrect:

> Adaptive unboxed vectors defined in `Data.Vector.Unboxed` are significantly more flexible at no performance cost.

### `Storable` vs `Primitive`

Instead of `Unboxed`, either `Storable` or `Primitive` should be promoted instead.

I'm still uncertain about the trade-offs of who should manage a vector's memory.

- Although `Storable` is originally an FFI thing, it still seems to do a fine job for general-purpose programming, and it doesn't have the 2x factor of GC-controlled memory, as opposed to `Primitive`.

- `MagicHash` is a recurrent point of confusion, and that also plays against `Prim` from a documentation/usability point of view.

I'm sure there are advantages to `Primitive`, but I'm not sure they are sufficiently strong to make it a better default than `Storable`.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.