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

Closed
skyfex opened this issue Jan 12, 2018 · 11 comments
Closed

anonymous struct literals #685

skyfex opened this issue Jan 12, 2018 · 11 comments
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@skyfex
Copy link

skyfex 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
Copy link
Sponsor Contributor

Hejsil 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 This issue suggests modifications. If it also has the "accepted" label then it is planned. label Jan 12, 2018
@skyfex
Copy link
Author

skyfex 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
Copy link
Member

andrewrk 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 This proposal is planned. 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
Copy link
Member

This is still planned but blocking on #287

@daurnimator
Copy link
Contributor

daurnimator 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
Copy link
Sponsor Contributor

hryx commented Apr 10, 2019

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

@emekoi
Copy link
Contributor

emekoi commented May 3, 2019

@andrewrk could you clarify which of the examples in the proposal would work after this is implemented?

@daurnimator
Copy link
Contributor

@andrewrk is this now unblocked with copy elision merged?

@emekoi
Copy link
Contributor

emekoi commented Aug 3, 2019

how is this different from #208?

@andrewrk
Copy link
Member

andrewrk commented Sep 4, 2019

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

I don't see why, is something on your mind?

@andrewrk could you clarify which of the examples in the proposal would work after this is implemented?

.{.field = value} syntax. It's like an anonymous enum literal, but for structs/unions.

@andrewrk is this now unblocked with copy elision merged?

Yes

how is this different from #208?

#208 is anonymous array literal. .{a, b, c}.

andrewrk added a commit that referenced this issue Nov 11, 2019
This implements stage1 parser support for anonymous struct literal
syntax (see #685), as well as semantic analysis support for anonymous
struct literals and anonymous list literals (see #208). The semantic
analysis works when there is a type coercion in the result location;
inferring the struct type based on the values in the literal is not
implemented yet. Also remaining to do is zig fmt support for this new
syntax and documentation updates.
andrewrk added a commit that referenced this issue Nov 11, 2019
This implements stage1 parser support for anonymous struct literal
syntax (see #685), as well as semantic analysis support for anonymous
struct literals and anonymous list literals (see #208). The semantic
analysis works when there is a type coercion in the result location;
inferring the struct type based on the values in the literal is not
implemented yet. Also remaining to do is zig fmt support for this new
syntax and documentation updates.
@andrewrk
Copy link
Member

Solved by #3652, landed in 5502160.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

6 participants