We now have anonymous tuples, but they can only be created at runtime using the var type. While this satisfies "varargs" and coercion to arrays / vectors, we still cannot define a tuple type at comptime.
Potential syntax:
const Point = tuple{ i32, u32 }; // explicit, but adds a keyword
const Point = struct{ @"0": i32, @"1": u32 }; // matches the backend structure but feels pretty gross
Packed tuples should also be possible since it can represent in memory data structures pretty well, like manually managed stacks.
Per https://github.com/ziglang/zig/issues/4335#issuecomment-580842164:
const Point = struct{ i32, u32 };
Another usecase for declared tuples: emulating multiple returns.
Do you foresee another .Tuple (vs .Struct) TypeInfo / TypeId for that (would intuitively advise against such) or have it more of a sugar building on the existing struct internals incl the existing "0", "1" etc auto-naming?
@typeInfo for tuples is the same as structs, but there will be an added flag is_tuple: bool
Pondering it some more..
Hate to bikeshed, truly do usually, but would propose an alternative to "yet another keyword": extend the lang syntax such that struct {i32, bool, void} becomes valid and parses into an is_tuple struct (as we know it exists underlying already from .{10, "hi", false} literals) --- with the easy-to-intuit rule that either all fields must be named or all must be unnamed. If the parser can be made to do tuple{x,y,...} surely it can be made to do struct{x,y,...} =) whether the default-values part in a StructFieldNode (or however it's called internally) following a = should stay in here or go, I can't assess well right now but the same question would be for a tuple{} keyword.
What bugs me about a new keyword? One subtle quality (among various) I liked about Zig is that there is kinda mostly / overall / in-spirit _one_ moniker for _one_ underlying-mechanism/operational-semantic, so to speak. Now you'd have to explain in docs/chats/blogs/teaching-materials/talks at length "there are unions, they are like this, then there are structs, they are like that, and btw but there's also another special kind of structs without field names, so there are in addition to unions and structs also tuples but they're really structs under the hood".
PLT folks love the "tuple" moniker but it was always understood that structs/records/classes/tuples are really all primarily one thing, product types. If a struct can have or lack a certain (programmer-facing only, no difference to machine or end user) quality of trivial difference, another keyword for "almost the same thing, except names" might be disproportionate.
@metaleap 's ammendum https://github.com/ziglang/zig/issues/4335#issuecomment-580842164 is also accepted
I don't understand what is being proposed. Is it a special way to declare struct without specifying field names? What is a packed tuple, i can't imagine that, there are no examples to visualise it.
As for use case "emulating multiple returns": i would expect multiple returns to be named, as arguments to a function must have names. e.g:
getPosition() struct {x: f32, y: f32}
divide(num: u64, denom: u64) struct {result: u64, remainder: u64}
getRegs() struct {bp: usize, ip: usize}
Also would anon list literals be coerceable to 'tuples'? @LemonBoy is saying that it should not be allowed https://github.com/ziglang/zig/issues/4148#issuecomment-580711878.
I don't understand what is being proposed.
Nor you have understood what I was saying as I was talking about coercing anonymous array literals to named struct literals where you really want to specify what field are you assigning a value to.
@LemonBoy your argument that it is a time-bomb does not justify your point, however i understand why it would not be allowed - "favour reading code over writing code"
Edit: how do 'tuples' fit into this zen?
Edit: how do 'tuples' fit into this zen?
@Rocknest understand this: we already have tuple literals: .{ 10, "20", 3.0 } (that creates under the hood the appropriate struct type with 3 fields named "0" through "2"), just no way to express in code the type of such a thing in current syntax. (For args, vars, consts, fields, you name it.)
The proposal is to have such a facility in the grammar.
@metaleap as i said https://github.com/ziglang/zig/issues/4335#issuecomment-580850792 i wonder what the use case would be, is that _really_ justified?
I feel like a separate key word may cause more confusion than it's worth - new users would likely see it and assume it's a different data structure. I like the idea of making a struct type without specifying field names though ie stuct{u32, []u8}. Nice for function return types. +1
@Rocknest from my experience in langs that have them (Haskell, C#), unnamed-field "tuples" rarely make it into a somewhat-matured final "production-grade" code product to any great extent. But IMHO they're invaluable just for more-rapidly-iterating alone already, as you restructure fn signatures quickly and re-mold your data/flow structurings at an ideally fast pace. That alone is a valid _programmer_ use-case, making the facility fit for every _programming_ language =)
Commonly seen is also the desire for what's hi-falutingly described as "heterogenous look-and-feel-like arrays" say Vec3[0] through Vec3[2] get accepted by compiler / type-checker, turning into field-accesses, even going as far as allowing loops. I won't argue with that, though in _my_ practice it doesn't really come up.
@Rocknest here's a real-ish example that I've encountered building my wasm interpreter:
var stack: [65536]u8;
pub fn i32Store(self: *core.Instance, pop: packed tuple{ u32, i32 }) void {}
pub fn i64Store(self: *core.Instance, pop: packed tuple{ u32, i64 }) void {}
A packed tuple is able to represent this data directly mapped inside of the memory space. Without packing, I'd have to copy the data out (which is fine but a little inconvenient), and without tuples I'd have to emulate it with struct fields (which is fine but I'd have to invent some incrementing name and have adhoc tuples).
We already have tuples in the language because it was needed to support varargs. This proposal simply gives it a name and a way to declare the type.
pub fn i32Store(self: *core.Instance, pop: packed tuple{ u32, i32 }) void {}
And here i am reading this code having no idea what first and second integers represent. Are you sure they do not need names?
The shared invocation context only knows the position and types. Inside the function, we can apply conceptual names, but exposing it means I cannot share the parsing logic (which is based solely in bytes).
Although thinking about it, I can possibly rely on a packed struct and iterating through the fields at comptime to generate the parser...
I feel like a built-in function would be the best way to define tuple types. Tuples are just a special case of structs after all, so special syntax might be a bit redundant.
const MyTuple = @TupleType(u32, i32);
const MyTuple2 = @TupleType(u64,u32,bool);
const MyPackedTuple = @PackedTupleType(u32,i32);
const x : MyTuple = .{32, -5};
const y : MyTuple2 = .{1,2,false};
Another crazy idea with tuples — we can sorta create generic structs at comptime:
pub fn Soa(comptime Struct: type) type {
const fields = std.meta.fields(Struct);
const Data = switch (fields.len) {
1 => struct { []fields[0].field_type },
2 => struct { []fields[0].field_type, []fields[1].field_type },
3 => struct { []fields[0].field_type, []fields[1].field_type, []fields[2].field_type },
else => @compileError("TODO"),
}
return struct {
data: Data,
allocator: *std.mem.Allocator,
pub fn initCapacity(allocator: *std, cap: usize) !Self {
var result = Self{ .data = undefined, .allocator = allocator };
inline for (fields) |field, i| {
// This type of generic access is currently inaccessible
// since we can't generate field names
self.data[i] = try allocator.alloc(field.field_type, cap);
errdefer allocator.free(self.data[i]);
}
return result;
}
}
@fengb is this what you are trying to achieve?
https://gist.github.com/Rocknest/6d07cec388036fcdf07fb4760cb92466
I like the separate keyword because it makes the intent more clear but am fine with either.
I've never seen really good use cases for tuples to be honest. In C++ they're super confusing, especially the way they're used in the standard library. In Python it's always tricky because you have to remember what goes where as it doesn't check anything for you. And then you want to print it because you have a bug somewhere and you're better of using namedtuple (which is like a struct, except you can't change the values once assigned).
I'm not opposed to it, but I do doubt it's usefulness.
As for multiple return values. I'm also not too fond to use tuples for that. We don't have tuples for multiple input values either, do we? If Zig wants multiple return values (and I do think that could be useful) then imho it would be better to name them (maybe name all return values?) and somehow refer to them inside the function.
Tuples were already added to replace vararg inputs. This proposal only exposes the ability to name the type so you can declare constrained tuples instead of being always generic.
But zig's 'tuples' are really just annonymous structs. This proposal goes in somewhat opposite direction.
How would this work with bigger tuple struct definitions? Like if I wanted a member function:
const MyTuple = struct {
i32,
[]const u8,
fn foo(this: @This()) void { ... }
}
Would I do this just like this?
@Rocknest tuples have the added abilities that they can be looped over, concatenated, and items can be accessed with array indexing syntax.
.{1, 2} ++ .{3}
inline for(.{1, 2, 3}) |item| {}
(.{1, 2, 3})[2]
RIght now enums, structs, and unions share the same syntax.
enum { u1, u2, u3 } is parsed as 3 identifiers, thus so is struct { u1, u2, u3 }
I think the solution is just to give structs its own syntax, but it's something to consider here.
Consider closing this since #6416 its possible to create tuple types in userland
Most helpful comment
Pondering it some more..
Hate to bikeshed, truly do usually, but would propose an alternative to "yet another keyword": extend the lang syntax such that
struct {i32, bool, void}becomes valid and parses into anis_tuplestruct (as we know it exists underlying already from.{10, "hi", false}literals) --- with the easy-to-intuit rule that either all fields must be named or all must be unnamed. If the parser can be made to dotuple{x,y,...}surely it can be made to dostruct{x,y,...}=) whether the default-values part in a StructFieldNode (or however it's called internally) following a=should stay in here or go, I can't assess well right now but the same question would be for atuple{}keyword.What bugs me about a new keyword? One subtle quality (among various) I liked about Zig is that there is kinda mostly / overall / in-spirit _one_ moniker for _one_ underlying-mechanism/operational-semantic, so to speak. Now you'd have to explain in docs/chats/blogs/teaching-materials/talks at length "there are unions, they are like this, then there are structs, they are like that, and btw but there's also another special kind of structs without field names, so there are in addition to unions and structs also tuples but they're really structs under the hood".
PLT folks love the "tuple" moniker but it was always understood that structs/records/classes/tuples are really all primarily one thing, product types. If a
structcan have or lack a certain (programmer-facing only, no difference to machine or end user) quality of trivial difference, another keyword for "almost the same thing, except names" might be disproportionate.