Last active
October 17, 2018 12:16
-
-
Save Hejsil/dc1ffefcdd8847dcd097208e633db4e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
[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:
@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.
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?