Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

anonymous struct literals #685

Open
skyfex opened this Issue Jan 12, 2018 · 6 comments

Comments

Projects
None yet
5 participants
@skyfex
Copy link

commented Jan 12, 2018

This is a proposal which isn't really intended to solve any significant issue with Zigs design (please provide an example of a use-case if you think this proposal is important), but it's something that could follow as a consequence of #683. If it is implement, it would probably be relatively easy to implement most of this proposal, and programmers might expect this behavior from the principle of consistency and DRY.

Proposal
Whenever Zig can infer a struct type name from the context of the code, and it makes sense to do so, it should.

For instantiations Zig could allow you to type .{field = value} instead of StructName {field = value}. It might make sense to drop ., but I'm using it for the examples to make it closer to #683. For method calls you could type .methodName(123) instead of StructName.methodName(123).

Examples

  1. When declaring a const or variable you could specify the type on either side of the assignment:
var a: StructName = .{.field = value};
var b = StructName {.field = value};
  1. When assigning to an already declared variable
b = .{.field = value};
  1. When assigning to a field in a struct
object.foobar = .{field = value};
object = .{foobar = .{field = value}};
  1. When calling functions
fn baz(t: StructName) { ... }
baz(.{field = value});
  1. When returning from a function
fn baz() -> StructName {
  return .{field = value};
}
  1. Switch statements is the real pickle here. If you follow this train of thought to its logical conclusion, you end up with some variant of a feature which is common in functional languages: "pattern matching". Here's a rough idea:
switch (person) {
  .{.gender = .Male } -> print("A {} year old male", person.age);
  .{.gender = .Female } -> print("A {} year old female", person.age);
}

In functional languages you can usually bind a field to a variable while doing pattern matching. Something like .{gender = male, .age = const age}. Probably the syntax should make it clearer that the assignment goes the other way than usual. Something like .age -> const age. But this might just not make sense in an imperative language. Don't get too hung up on the details here though, it should probably be its own proposal.

  1. When calling a method that returns a struct, you could infer the struct type name
const Thing = struct {
    x: f32,
    pub fn init(x: f32) -> Thing {
        return Thing { .x = x };
    }
};
var t: Thing = .init(0);
pub takeThing(t: Thing) {...}
takeThing(.init(0));
pub makeThing() -> Thing { return .init(0); }
etc., etc.

Discussion

Did I miss any examples?

Example 1 might be too much in violation of "One way to do things". Neither way is better than the other.

It might not make sense for example 3 and 4. For the other examples, the struct type name is easily visible in the nearby code. But for function calls and field names it's ofte far away. That's bad for readability.

I would say that example 2 and 5, the shorthand is objectively better in most cases. But it makes sense to still allow you to specify the struct type name if the declaration is too far away. (Just as you could now choose to do var a = getThing() if getThing is in your current namespace and/or the type is obvious from the function name , or var b: Thing = foobar() if foobar is in some other file.)

The same goes for example 7. For function calls it may be too unreadable, for assignments and returns it makes sense.

@Hejsil

This comment has been minimized.

Copy link
Member

commented Jan 12, 2018

Agreed with your points on 1/2/5/6.

3: Well, this was one of your use cases for inferred enum so that's a consideration. I think the same arguments apply to both these cases. This also applies to 4.

7: Is the rule that the "method" has to be in the types "namespace", and return the type too? It's quite limiting, but it does make the init pattern quite nice in certain cases (like initializing arrays of structs).

const a = []ArrayList(u32){
    .init(allocator),
    .init(allocator),
    .init(allocator),
    .init(allocator),
}

Other than large array initialization I don't see the issues this solves either.

@andrewrk andrewrk added this to the 0.3.0 milestone Jan 12, 2018

@andrewrk andrewrk added the proposal label Jan 12, 2018

@skyfex

This comment has been minimized.

Copy link
Author

commented Jan 12, 2018

@Hejsil : "7: Is the rule that the "method" has to be in the types "namespace", and return the type too? " — That's the only way I could see it working, yes. I agree that it doesn't seem to solve a lot of real-world use cases. It's just a possibility. Someone needs to elaborate on this use-case if they think it's necessary.

@andrewrk

This comment has been minimized.

Copy link
Member

commented Jan 12, 2018

One quick reminder, since we have compile time function execution, you can use it to initialize arrays, like this:

const a = []ArrayList(u32) { arr(), arr(), arr(), arr() };
fn arr() -> ArrayList(u32) {
    return ArrayList(u32).init(allocator);
}

In fact I think in this case it's even safe to do:

const a = []ArrayList(u32) {ArrayList(u32).init(allocator) } ** 4;

@andrewrk andrewrk modified the milestones: 0.3.0, 0.4.0 Feb 28, 2018

@andrewrk andrewrk added the accepted label Nov 21, 2018

@andrewrk andrewrk changed the title Type inferrence for struct type names anonymous struct literals Feb 28, 2019

@andrewrk andrewrk modified the milestones: 0.4.0, 0.5.0 Feb 28, 2019

@andrewrk

This comment has been minimized.

Copy link
Member

commented Feb 28, 2019

This is still planned but blocking on #287

@daurnimator

This comment has been minimized.

Copy link
Contributor

commented Apr 10, 2019

Example use case from here

const lib = []lua.luaL_Reg{
    lua.luaL_Reg{ .name = c"func_void", .func = wrap(func_void) },
    lua.luaL_Reg{ .name = c"func_bool", .func = wrap(func_bool) },
    lua.luaL_Reg{ .name = c"func_i8", .func = wrap(func_i8) },
    lua.luaL_Reg{ .name = c"func_i64", .func = wrap(func_i64) },
    lua.luaL_Reg{ .name = c"func_f16", .func = wrap(func_f16) },
    lua.luaL_Reg{ .name = c"func_f64", .func = wrap(func_f64) },
    lua.luaL_Reg{ .name = c"bar", .func = bar },
    lua.luaL_Reg{ .name = 0, .func = null },
};

It would be great to avoid the repition of lua.luaL_Reg.

@hryx

This comment has been minimized.

Copy link
Contributor

commented Apr 10, 2019

Oh, that brings up a good point. Will C interoperation present any challenges for this feature?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.