In Swift's case, there's already a decent amount of code out there using UnsafeBufferPointer
for bulk data operations, and the underlying array value witness operations in the runtime that they use assume that an "array" is stride-padded. We want new contiguous collection types to be compatible with existing practice, and Span
to be a mostly drop-in replacement for unsafe types, so IMO we should stride-pad any type that's used for array storage.
Was playing with this over the past week using the ValueGenerics experimental feature. I discovered that almost the first thing I wanted to do was make use of integer parameters variadically, but that this was clearly denoted as a future direction rather than as part of the overall design.
As a (future) review comment, it seems odd to introduce this feature and special case treating these parameters unlike all others. It would seem much more consistent to allow me to specify an integer parameter in any context where I specify a non-integer param.
The only case I can say I’ve encountered in my code is initializing a UUID from a bunch of integers. Which begs the question: even if we can do _vector
for struct properties, how will functions be handled?
i'm curious could you elaborate on what you are thinking in re funcs?
I'm very excited to discover this pitch and see the progress towards Swift's native support for fixed-size arrays.
I was starting to despair that fixed-size array support would ever come.
And a big yes the sooner we have the possibility of importing fixed-size array from C, the better it will be even if this causes a break in source compatibility given the pain and limits of the current import in the form of tuples.
FWIW, I'm cautiously optimistic that we can adopt eventual fixed-size array importing without breaking source compatibility.
Why couldn't we have the disambiguation be determined by Self
? We already have common methods for disambiguation with self
and these parameters are tied to the type. Having just read the Generalized Collection APIs
section for the Vector pitch, it looked completely off to me. Now, the problems to solve is whether the self
or Self
usage gets inferred to omit the prefix and how clashes would be used on instances of the type... maybe living within the meta type Type
/think
What we all know and use
struct Foo {
let bar: Int
func doStuffWithBar() {
var bar = self.bar
// stuff
}
}
Vector with count
and Collection.count
:
struct Vector<let count: Int, T> { /* ... */ }
extension Vector where Element: ~Copyable {
// ....
public var count: Int { Self.count }
}
Unfortunately, we can’t refer to a type’s generic parameters using Self.
since they’re not actually static properties of the type. So, in your example, Self.count
does not exist. To prove this using current Swift, the code below:
struct S<T> {
static func foo() {
print(Self.T.self)
}
}
S<Int>.foo()
Fails to compile with the following message: type 'S' has no member 'T'. (Of course, it would work if we wrote T.self
instead of Self.T.self
, but then we're back where we started in terms of disambiguating Vector
's count
.)
So, to use the lowerCamelCase convention on Vector
’s integer generic parameter, I believe we’d have to call the generic parameter something else like size
, length
, capacity
, fixedCount
, etc.
Well, this is allowed:
struct Vector<count> {
var count: Int { MemoryLayout<count>.size }
}
which suggest that this should also be allowed:
struct Vector<let count: Int> {
var count: Int { count }
}
no?
This is the first proposal where a value is a property of a type, it seems like they should be. At the same time, I think of this as a way to just combat the potential ambiguation we will face.
I haven't looked throughout the forums, but almost feel like there should be a way to expose generic parameter types like the example syntax. (Edit: er, pre-some
syntax, maybe not anymore)
It's great to see this finally proposed after four and a half years
I would rather write the first example in this proposal as:
struct Vector<N, T> where N: SingleInt {
/*implementation TBD*/
}
in which N
is a (singleton) type, which happens to contain only one value, and this value happens to be (interpreted as) an Int
.
What is the need for the special syntax let N: Int
instead of just N
and a conformance to a protocol? I don't know the implementation details, maybe I miss something.
A few comments on the above syntax:
N
is a type, not a value, so it is capitalized.let n: N
is a valid declaration. No need for an assignment, becausen
can take only one value (and it can be eliminated in optimization).var n: N?
is a valid declaration, which assigns the default valuenil
.- The single integer value in
N
can be expressed with something likeN.value
(similar toInt.max
).
Regarding parameter arithmetic: this is something special to this kind of "types", and maybe the most important feature. The expression N + M
, where N, M: SingleInt
can be considered as type arithmetic as well. The result is another type, also conforming to SingleInt
(assuming there is no overflow).
Regarding future directions: there is UInt
for non-negative integers, but little support for other common integer ranges. It would be nice to have something like 1+UInt
for positive (and non-zero) integers. Also the trivial singleton type ZInt
which contains only 0
.
With some (limited) type arithmetic, a few common types could be expressed:
n + ZInt
is the type containing onlyn
n + UInt
is the type of integers greater than or equal ton
n - UInt
is the type of integers less than or equal ton
2 * UInt + 1
is the type of positive odd integers
The type 1+UInt
of positive integers is not the same as the range 1...
of positive integers. The first is a type, while the latter is a value. A value of type 1+UInt
is a single integer, not a range.