zig icon indicating copy to clipboard operation
zig copied to clipboard

Proposal: Replace _ with unused keyword

Open SpexGuy opened this issue 3 years ago • 22 comments
trafficstars

Zig currently uses _ in five places:

  • marking a parameter as unused (fn foo(_: u32) void)
  • marking an expression value as unused (_ = func();)
  • marking a named variable as unused (_ = variable;)
  • marking an enum as extensible (enum (u8) { _ })
  • declaring a padding field (_: u6,)

This proposal is to replace the first three uses with a new keyword, unused. It behaves as follows:

  • On parameters, fn foo(unused index: u32) void

Allowing a name here can improve documentation, and make it easier to notice if the parameter is actually needed. However, sometimes names are not needed, so we could also allow the syntax fn foo(unused: u32) void. Since unused is a keyword, this is not shadowing and can be properly identified by the parser.

  • On locals, unused can be used in the place of const or var. As in,
unused result = func(); // result can be named for better documentation
unused = func(); // name is optional
unused result: u32 = func(); // type annotations are also allowed
unused: u32 = func(); // even without a name

unused = variable; // this could be a compile error if `variable` is a local or parameter, unused should instead be placed on the declaration of variable.

As you might expect, attempting to use a variable declared as unused is a compile error.

This change is breaking, but zig fmt would be able to perform most of the update correctly.

After this change, _ would be used exclusively for extensible enums and padding fields.

SpexGuy avatar Nov 29 '21 20:11 SpexGuy

I like this very much.

ikskuh avatar Nov 29 '21 20:11 ikskuh

unused = func(); // name is optional
unused = variable;

can you share more about why this and not:

unused func(); // name is optional
unused variable;

edit: ah I see it's kind of like a variable declaration without the type.

andrewrk avatar Nov 29 '21 20:11 andrewrk

Yeah I think that might be ambiguous, but if you think it's cleaner I'm open to it. Edit: The specific case of unused variable; could be a compile error, which would resolve the ambiguity from a reader's perspective. Whether it's ambiguous for the parser is a different question.

SpexGuy avatar Nov 29 '21 21:11 SpexGuy

I suggest that other variable modifiers should be illegal when in front of unused. That is, const unused = func() should result in an error during parsing. Same with var.

This would make it clearer to users that they shouldn't use unused as a plain variable name. It would help clarify that something like this is totally forbidden:

const parking_spaces = ...
const unused = checkForVacancies(parking_spaces);

spenczar avatar Nov 29 '21 21:11 spenczar

I think it would be clearer for unused to be a modifier, and not an identifier. If it's just a modifier, then things like unused result = func() are allowed, but not unused = func().

Identifiers have tons of places where they are normally legal, but it's not clear how they'd work with unused:

  • return unused
  • val == unused
  • array[0..unused]
  • unused => 10 in a switch

These all seem like they clearly should be illegal. unused isn't really like a normal identifier, and making it one seems a little inconsistent. That inconsistency could be avoided by making it a modifier like const.

spenczar avatar Nov 29 '21 22:11 spenczar

The _ = foo; syntax is also used to reference decls so that they get analyzed for unit tests. I think this change would make this use case read much worse. I do think it is an improvement for function parameters though.

ifreund avatar Nov 30 '21 00:11 ifreund

I generally like this idea but don't like the suggested syntax.

Under the current syntax, you just do _ = foo and _ = bar(). It just simply means that you don't care about what it returns, let alone what type it is. I do not get why "optional name and type" is suggested here. It makes it look like the same keyword is being used as an identifier as well as a modifier. My preferred syntax is the one andrew was talking about, i,e

unused foo;
unused bar();

which makes it more familiar and similar to the current syntax. Yes it does not allow tagging the type or having a name, but I don't see what benefits it provides. Let me know.

iddev5 avatar Nov 30 '21 15:11 iddev5

This has the added advantage of freeing _ for #5893 see @Rocknest's proposal

Mouvedia avatar Dec 01 '21 16:12 Mouvedia

Does this also affect the capture syntax?

for (values) |_ , i| print(i); ->
for (values) |unused , i| print(i);

if (maybe) |_| "Not null" else "Null"; ->
if (maybe) |unused| "Not null" else "Null";

if (trySomething()) |_| "Success" else |_| "Error"; ->
if (trySomething()) |unused| "Success" else |unused| "Error";

raulgrell avatar Dec 02 '21 23:12 raulgrell

imo this change would make code harder to read - I feel like personally I'd be more likely to miss yet another key word when reading over code compared to something that looks more like a symbol, and the behavior of _ seemed pretty logical (but that might just be me).

apppppppple avatar Dec 07 '21 00:12 apppppppple

unused as a word has no effect on control flow. I don't particularly see the problem with leaving things as _, particularly when adding another keyword for this might make it look like part of the variable name.

also seems odd this got the accepted label even when there were open questions

nektro avatar Dec 25 '21 20:12 nektro

I think the keyword discard is way clearer. It clearly states the intent that you want to evaluate something, but ignore its value. Makes it much clearer when you're discarding a function return value or referencing something to force it to be generated.

With that said, I don't think either is an improvement over the current _ = syntax, and for the function parameters, a big downgrade.

N00byEdge avatar Jan 02 '22 16:01 N00byEdge

I think the _ = syntax is much better than the other alternatives such as unused or discard. If things don't matter towards understanding what code does, they should avoid taking up screen space.

My conceptual understanding of the _ symbol right now is akin to "I don't care about this. Compiler, you figure it out".

Some things just end up being boilerplate and it's useful to have one symbol that always signifies to the compiler to guess the correct thing to do in this situation.

In OP's post, all 4 out of 5 examples have one thing in common. The programmer doesn't care about the values in the sense that they won't be used. The only one that's slightly different is this one:

marking an enum as extensible (enum (u8) { _ })

If necessary, I think it's this syntax that should change, rather than the others.

ryancsh avatar Jan 07 '22 17:01 ryancsh

If necessary, I think it's this syntax that should change, rather than the others.

Id expect ....

Mouvedia avatar Jan 08 '22 00:01 Mouvedia

I think the _ = syntax is much better than the other alternatives such as unused or discard. If things don't matter towards understanding what code does, they should avoid taking up screen space.

@andrewrk I see you un-accepted this after the above comment.

I just wanted to point out that it would be very valuable to have a compiler error for variables with _ = xxx that are used. People tend to forget to remove the _ = xxx, making them misleading to readers. But with the current syntax, such an error may conflict with other uses of _. So perhaps a new unused keyword is justified, along with the compiler error for using such a variable.

jumpnbrownweasel avatar Feb 06 '22 22:02 jumpnbrownweasel

I just wanted to point out that it would be very valuable to have a compiler error for variables with _ = xxx that are used. People tend to forget to remove the _ = xxx, making them misleading to readers. But with the current syntax, such an error may conflict with other uses of _. So perhaps a new unused keyword is justified, along with the compiler error for using such a variable.

I don't see how your suggestion makes things better. Compare:

_ = xxx;   // intend to not use
a = xxx;   // variable xxx is used here

vs.

unused = xxx;   // intend to not use 
a = xxx;        // variable xxx is used here

It's still just as misleading.
Any compiler checks for detecting use after assigning to unused should just as easily be applicable to _.

ryancsh avatar Feb 07 '22 03:02 ryancsh

Any compiler checks for detecting use after assigning to unused should just as easily be applicable to _.

Sorry, I wasn't clear and it is possible I'm wrong. I jumped to the conclusion that such compiler checks with the _ syntax may be problematic. Because _ is used for several different purposes, _ = xxx this may not be known by the compiler to mean "unused" but rather may simply work to suppress the unused warning. In that case, the compiler check for detecting usage of such variables would be problematic.

If that is not the case, then my point is invalid.

jumpnbrownweasel avatar Feb 07 '22 15:02 jumpnbrownweasel

I am just running in to this with zigler, where I am "ad-libbing" certain variables in codegen'd stuff. For this use case, it would be super useful to allow both the keyword in the function call args definitions as well as the in-body version.

Immaterial to my use case, I slightly prefer:

fn myFun(discard a: u32, b: u32) void {
  discard b;
  discard someFunctionCall();
}

ityonemo avatar Mar 21 '22 18:03 ityonemo

I think unused could be used before any decl:

unused fn myFunc() void {
}
unused const myDecl = comptime blk: { }

it would fix most of the problems #335 Why i think it would be good:

  • It's better than commenting out because it leaves it as valid code that the ide can color, and that the langage server can get info from (and maybe the doc generator?)
  • It's better than _ = foo or the above proposal of unused = foo; because it would forbid use of the decl so it's not possible to forget removing it
  • It could be reserved for non pub decls
  • It would suppress the errors proposed in #335 for unused decls when you use that keyword and discourage other methods of suppressing that error such as just adding pub

Ps: should I make an other proposal for this?

Guigui220D avatar May 12 '22 13:05 Guigui220D

Zig currently uses _ in five places:

but what about if/while/for "captures"? I mean

for (smthng) |_,_| {...}

and alike.

dee0xeed avatar Oct 21 '22 16:10 dee0xeed

that's already possible, its 6 then

nektro avatar Oct 21 '22 18:10 nektro

that's already possible, its 6 then

I meant

for (smthng) |_child,_index| {...}
``

dee0xeed avatar Oct 21 '22 18:10 dee0xeed

How about the keyword trash instead of unused or discard? It could be better as it has the same length as const, but it sounds a little silly. Example:

const out = std.io.getStdOut().writer();
trash try out.write("Hello, world!\n");

anyputer avatar Feb 23 '23 17:02 anyputer