Skip to content

Instantly share code, notes, and snippets.

@Hejsil
Last active October 17, 2018 12:16
Show Gist options
  • Save Hejsil/dc1ffefcdd8847dcd097208e633db4e2 to your computer and use it in GitHub Desktop.
Save Hejsil/dc1ffefcdd8847dcd097208e633db4e2 to your computer and use it in GitHub Desktop.
const assert = @import("std").debug.assert;
test "Tuples!" {
// Like enums, structs and unions, tuples will have their own keyword for declaring
// tuple types.
const Tuple = tuple.{i32, []const u8};
const Struct = struct.{a: i32, b: []const u8};
const Enum = enum.{A, B};
const Union = union.{A: i32, B:[]const u8};
// Two tuples types are the same, as long as all their members are the same. This
// is different from how structs, enums and unions work,
assert(tuple.{u8} == tuple.{u8} );
assert(struct.{a:u8} != struct.{a:u8});
assert(enum.{A} != enum.{A} );
assert(union.{A:u8} != union.{A:u8} );
// Tuples use the array initializer syntax, but each element can be of different
// types. No new initializer syntax.
const t = tuple.{i32, []const u8}.{0, ""};
const a = [2]u8.{0, 1};
// To access the items of a tuple, the index operator is used.
// This allows tuples to be used with inline while, to iterate over all members.
const a = t[0]; // Here, the index have to be comptime known
// They also implicitly cast to arrays/slices, if all their elements are the same.
const a: [2]u8 = tuple.{u8, u8}.{0, 0};
const s: []const u8 = tuple.{u8, u8}.{0, 0};
// Tuples can be destructured into multible variables.
// This allows for multible return types from both functions and labled blocks:
const min, const max = blk: {
var arr = []u8{2,3,5,2};
var min = arr[0];
var max = arr[0];
for (arr[1..]) |i| {
if (i < min)
min = i;
if (max < i)
max = i;
}
break :blk tuple.{u8, u8}.{min, max};
};
assert(min == 2 and max == 5);
// We might even allow assignment into already declared variables:
var a: u8 = undefined;
a, const b = tuple.{u8, []const u8}.{0, ""};
// *** None essential features ***
// We might also allow the "++" operator on tuples:
const t1 = tuple.{u8}.{0};
const t2 = tuple.{[]const u8}.{""};
var t: tuple.{u8, []const u8} = t1 ++ t2;
}
test "Inferred initializers" {
// Inferred initializers are initializers whos types are inferred from the context
// in which they are used. They allow for shorthand initialization where it does
// not improve readablilty to append the initializer with a type.
// Inferred initializers come i 3 syntactic flavors.
// structs and unions will use "inferred struct initializers"
const s: struct.{a: i32, b: []const u8} = .{ .a = 0, .b = "" };
const u: union.{A: i32, B: []const u8} = .{ .A = 0 };
// tuples and arrays will use "inferred tuple initializers"
const t: tuple.{i32, []const u8} = .{0, ""};
const a: [2]u8 = .{0, 1};
// enums will use "inferred enum initializers"
const e: enum.{A, B} = .A;
// They will have their own internal temporary type, which implicitly cast to
// other types using the following rules:
// * "inferred struct initializers" will implicitly cast to:
// * Structs, If:
// * The initializer has the same number of fields as the struct.
// * All initializer field names are also in the struct.
// * All initializer fields implicitly casts to the struct field of the
// same name.
// * Unions, If:
// * The initializer has 1 field.
// * The initializers fields name is contained in the union.
// * The initializers field implicitly casts to the tag of that name.
// * "inferred tuple initializers" will implicitly cast to:
// * Arrays, If:
// * The initializer has the same number of items as the array.
// * All initializers items implicitly cast to the arrays item.
// * Tuples, If:
// * The initializer has the same number of items as the tuple.
// * All initializers items implicitly cast to the tuples items.
// * They will also cast to a normal tuple, if none of the above is true.
// * This is useful for passing inferred tuple initializers to functions
// taking "var".
// * "inferred enum initializers" will implicitly cast to:
// * Enum, If:
// * The initializers name is contained in the enum.
// * Union, If:
// * The initializers name is contained in the union.
// * The initializers name in the union is "void".
// In userspace, inferred initializers doesn't have a type. You therefore can't
// call @typeOf on them. They also can't be stored at runtime because of this.
// error: enum initializers doesn't have a type.
_ = @typeOf(.E);
// error: enum initializers doesn't have a type
_ = funcTakingVar(.E);
// error: .E does not have a runtime representation.
var a = .E;
// error: struct initializers doesn't have a type.
_ = @typeOf(.{ .a = 2, .b = 4 });
// error: struct initializers doesn't have a type.
_ = funcTakingVar(.{ .a = 2, .b = 4 });
// error: .{ a: u8, b: u8 } does not have a runtime representation.
var a = .{ .a = u8(2), .b = u8(4) };
// The exception here is inferred tuples, which will cast to a normal tuple when
// it is not inferred.
_ = @typeOf(.{ 2, 4 });
_ = funcTakingVar(.{ 2, 4 });
var a = .{ u8(2), u8(4) };
}
// These two features allows us to have:
// *** Varargs ***
// With these two features, we can remove varargs, and have std.debug.warn be
// used like this:
fn warn(comptime fmt: []const u8, tuple: var) void { ... };
test "warn" {
// This proposal does not solve passing comptime_int to varargs functions
// VVVVV
warn("{} {}", .{ u8(1), "yo" });
}
// This also allows us to have "typed varargs" functionality:
fn concat(args: [][]const u8) []const u8 { ... };
test "concat" {
const res = concat(.{"Hello", " ", "World!\n"});
}
// *** Named arguments ***
// related: https://github.com/ziglang/zig/issues/479
const Flags = struct {
turn_on_nuke: bool,
go_full_screen: bool,
take_over_the_univers: bool,
};
fn something(flags: Flags) void { ... }
test "named args" {
// With https://github.com/ziglang/zig/issues/485, we even get default argument
// (https://github.com/ziglang/zig/issues/484) values.
something(.{
.take_over_the_univers = true,
.turn_on_nuke = true,
.go_full_screen = false,
});
}
@andrewrk
Copy link

andrewrk commented Oct 16, 2018

  • what do you think about [a, b, c] for the inferred initializer syntax of tuples?

I think I have a workable plan for the comptime/runtime thing. The idea is:

  • don't try to solve @inlineCall / @noInlineCall / adding @call with tuples yet. Just let those continue to be special case var args. Builtin functions can be var args, deal with it 😎
    note: these are not really even "var args" in the same sense as other functions. They are just special function calling syntax.
  • tuples are either all comptime or all runtime, just like structs, no hacks

Functions that accept a (non-comptime) tuple, the values are all runtime known, but the length of the tuple and the types is comptime-known, so you can iterate over it. I think that solves the problems, yeah?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment