While reading through the new JS explainer in #686, I found myself going back and forth on whether to use typed arrays for list<num>. For list<u8> it seems fairly natural to map that to Uint8Array, but less so for other number types. Like, it's probably quite a footgun to return Int32Array for list<s32>, since most JS devs don't use typed arrays very often, and typed arrays look juuuuuust enough like normal arrays to do some really surprising things:
> new Int32Array([1, 2, 3]).map(n => `item ${n}`)
Int32Array [0, 0, 0]
Surely no one would complain about this.
But that got me thinking - the main difference seems to be that sometimes you want a list of numbers, using whatever list type is idiomatic, but sometimes you just want a blob of bytes. Sure, you can have Int32Array and Float64Array and so on, but at least 95% of the time in JS you just want Array. And after further reflection, I think this is true of other languages too! In C++, do you really want a std::vector<uint8_t>, or would you maybe prefer std::string or some other kind of slice type? In Java, do you want List<Byte> or byte[]? In Python, do you want list or bytes? In Rust, do you want Vec<u8> or &[u8]?
(This is not true for some languages, e.g. Go would always do []byte, but that's fine, the distinction doesn't have to be meaningful in every language.)
So, purely as a hint to bindings generators, I think there is probably some value in having a bytes type that is in every way just an alias for list<u8>. Certainly there is no need for it to be different at the ABI level.
If accepted, I would suggest the following for the JS mapping:
list<T> is returned as a JS array, and as a param only accepts a JS array (...or iterable? idk)
bytes is returned as a Uint8Array, and a param accepts anything the TypedArray constructor would (which encompasses all typed arrays, ArrayBuffers, and JS iterables)
While reading through the new JS explainer in #686, I found myself going back and forth on whether to use typed arrays for
list<num>. Forlist<u8>it seems fairly natural to map that toUint8Array, but less so for other number types. Like, it's probably quite a footgun to returnInt32Arrayforlist<s32>, since most JS devs don't use typed arrays very often, and typed arrays look juuuuuust enough like normal arrays to do some really surprising things:Surely no one would complain about this.
But that got me thinking - the main difference seems to be that sometimes you want a list of numbers, using whatever list type is idiomatic, but sometimes you just want a blob of bytes. Sure, you can have
Int32ArrayandFloat64Arrayand so on, but at least 95% of the time in JS you just want Array. And after further reflection, I think this is true of other languages too! In C++, do you really want astd::vector<uint8_t>, or would you maybe preferstd::stringor some other kind of slice type? In Java, do you wantList<Byte>orbyte[]? In Python, do you wantlistorbytes? In Rust, do you wantVec<u8>or&[u8]?(This is not true for some languages, e.g. Go would always do
[]byte, but that's fine, the distinction doesn't have to be meaningful in every language.)So, purely as a hint to bindings generators, I think there is probably some value in having a
bytestype that is in every way just an alias forlist<u8>. Certainly there is no need for it to be different at the ABI level.If accepted, I would suggest the following for the JS mapping:
list<T>is returned as a JS array, and as a param only accepts a JS array (...or iterable? idk)bytesis returned as aUint8Array, and a param accepts anything the TypedArray constructor would (which encompasses all typed arrays, ArrayBuffers, and JS iterables)