Vector, a fixed-size array

There is no need to spitball anything. Perhaps I wasn't clear enough, but my post already lists the members of the vector family: they're Vector, Span, UnsafeBufferPointer and UnsafeMutableBufferPointer.

We're planning to soon add MutableSpan; it'll be extending this list. I don't believe there are any other vector-like constructs in consideration.

As @glessard explained, no. Neither Span nor Vector are appropriate choices to represent the raw backing store of a resizable data structure, because both of these constructs require their contents to be fully initialized. (Plus Span is non-escapable.) We do not expect to ever add operations that punch uninitialized holes inside Vector, just like we're very unlikely to add an operation that deinitializes a single member of a tuple.

High-performance low-level data structure implementations need to be able to manage initialization of their own storage in whatever way they see fit. An array type wants to compress its initialized items at the start of its storage, so bookkeeping just requires a count; a ring buffer type like Deque needs to add a start offset; a hash table with open addressing requires a full bitmap; etc. etc. The construct that is the greatest common divisor of all these is UnsafeMutableBufferPointer.

We can choose to implement Vector and Span in terms of operations on UnsafeMutableBufferPointer. Whether we end up doing this is an engineering decision for the stdlib, mostly outside of the Swift Evolution process. (Span in particular may end up rolling its own builtins, as it has unique exclusivity guarantees that can enable future compiler optimizations that wouldn't be possible to do with regular pointers.)

Yes; embedded use cases are indeed the primary driving force behind this work. (Like most work on the Swift performance predictability roadmap.)

Vector has near-universal appeal though. It adds a new core operation to Swift's type algebra, exponentiation.

Vector<5, Int> is literally 5 integers glued together and treated as a unit. This has the same* shape as (Int, Int, Int, Int, Int). But it's so much more! Treating it as a distinct, named construct gives us extra power: we can give it custom member operations; we can conform it to arbitrary protocols; we have the full flexibility of Swift to make it integrate well into our library idioms.

(* In spirit. It turns out that homogeneous tuples do not have precisely the same memory layout as what we expect of a vector type -- AIUI the final item isn't fully padded. Vector resolves this vexing issue.)

I expect to see Vector types frequently pop up as components of custom types, and as input/result types of custom functions -- not just in novel use cases like embedded platforms, but everywhere, throughout all Swift.

(Vector is necessary for embedded platforms to embrace Swift, but it's probably not sufficient. To progress things beyond the 1970s, we'll eventually also want to add an actual array type with inline storage. In our drafts we're calling that separate type InlineArray<N: Int, Element>. It has a fixed capacity of N items of type Element, and it works like a true array, with insert/remove operations.

Unlike Vector (which has near-universal appeal) InlineArray would be specifically targeting "embedded"-style and/or extraordinarily high-performance use cases -- cases that need/want to avoid dynamic allocations at all cost. It isn't clear yet if we'd want to add InlineArray to the core stdlib; given its far narrower role, it may be better to propose it in a module that needs to be explicitly imported.)

Courses on linear algebra are core parts of the curriculum for most computer science studies. I am well aware of what vectors are in computer graphics and machine learning.

The Vector name is so apt precisely because this type would be directly usable as a vector representation in a Swift linear algebra package.

I say "a vector representation" rather than "the vector representation", because I expect a production library will generally also need to offer other vector forms. (Sparse vectors, strided vectors, vectors with heap-allocated storage, etc., etc.) This does not provide grounds for objection, though: the vector representation we're proposing is the most natural one, and it deserves to carry the unqualified name.

We are not proposing vector space operations at this time. I already detailed the reasons behind this:

I look forward to seeing how far our package ecosystem can go towards resolving or working around these issues.

Unlike C++'s broken std::vector, our proposed Vector type is fully capable of becoming the core representation of vectors in a Swift linear algebra module. This is part of why I think this name is so apt.

The quoted passage simply reiterates Stepanov's opinion that using the term "vector" to mean a "dynamically re-sizable array" was incorrect. We wholeheartedly agree with this position; that is why we quoted it in the pitch document.

That very same document defines "vector" as follows:

  1. A matrix is a rectangular array of numbers, symbols, or expressions, arranged in rows and columns. [...]
  2. A vector is a matrix with only one row or one column. Although the vector is traditionally introduced as a tuple of scalars, and a matrix as a tuple of vectors, as far as theorems of linear algebra are concerned there is no difference between a single column matrix and a column vector, nor a single row matrix and a row vector.

Our proposed type fully embraces this definition. It abstracts it in precisely one way: Vector allows its elements to be of arbitrary types. I don't see how this would be a problem for linear algebra packages -- the operations they provide will simply constrain the element type to match scalars in whatever approximation of a vector space they are using.

This abstraction is the only practical choice -- I don't expect the Standard Library would want to mess with trying to define a hand-wavy concept like "numbers, symbols, or expressions". And of course it enables the use of the Vector type to escape the strict confines of specialized linear algebra libraries. Applying existing words to new contexts is a common form of evolution in all human languages. (In our case, this evolution has already happened, decades ago.)

People's expectations are sometimes far removed from reality. I do not believe we will ever need/want to add a concatenation operation to Vector, under any name. I cannot prove this belief; but I can point out that (as far as I know) the existing vector types in C and C++ (T[] and std::array<T, N>) do not provide such an operation, either. Not just one spelled with +, but vector concatenation in any form.

Given that + over vectors can be technically interpreted in two different ways, I think the stdlib is unlikely to define such an operator -- but this should not by no means prevent other modules from providing it!

That said, Swift's binary operators are not in a great place -- they have unresolved, major design issues that generally prevent public library code from exposing custom operator definitions.

I'd very much like to see libraries define vector addition, scalar multiplication etc. over Vector, because I'm hoping one implementation would become definitive enough to include in the stdlib. But given Swift's issues with operators, I expect successful libraries would rather represent these operations with named member methods, not infix operators:

extension Vector where Element: AdditiveArithmetic {
  func adding(_ other: Self) -> Self
}
extension Vector where Element: Numeric {
  func scaled(by factor: Element) -> Self
}

The real challenge here is to make sure expressions like a.adding(b.scaled(by: 3)) will get compiled into efficient enough machine code -- our experience with the stdlib's SIMD vector types strongly indicates that we cannot rely on optimizer heuristics for reliable vectorization. (Aside: oh hey, there is that word again!)

(One interesting research area would be to use macros to implement custom expression-level vectorization; perhaps that'll also enable the (re)introduction of infix operator syntax.)

This work is independent of the core definition of the Vector type itself. The proposed type will be the canonical representation of a vector in Swift, with deep compiler integration which will be impossible to replicate outside the stdlib.

23 Likes