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

proposal: Go 2: empty as an alias for struct{} #57825

Closed
jimsnab opened this issue Jan 16, 2023 · 23 comments
Closed

proposal: Go 2: empty as an alias for struct{} #57825

jimsnab opened this issue Jan 16, 2023 · 23 comments
Labels
Milestone

Comments

@jimsnab
Copy link

jimsnab commented Jan 16, 2023

Very simple proposal here.

Instead of:

// var m map[int]struct{}
m := map[int]struct{}{}
m[0] = struct{}{}

how about

// var m map[int]empty
m := map[int]empty{}
m[0] = empty{}

I love the change from interface{} to any, making code, stacks, intellisense, etc., easier to read.

I dislike the struct{}{} to represent the concept of an empty value.

I propose empty because it reduces curly brace clutter, and makes it obvious that the value is nothing.

@gopherbot gopherbot added this to the Proposal milestone Jan 16, 2023
@ianlancetaylor ianlancetaylor changed the title proposal: keyword 'empty' for struct{} - uniform with 'any' for interface{} proposal: v2: keyword 'empty' for struct{} - uniform with 'any' for interface{} Jan 16, 2023
@ianlancetaylor ianlancetaylor added LanguageChange v2 A language change or incompatible library change labels Jan 16, 2023
@ianlancetaylor
Copy link
Contributor

I suspect that interface{} is used a lot more often than struct{}.

@emmyforeignsss

This comment was marked as spam.

@seankhliao seankhliao changed the title proposal: v2: keyword 'empty' for struct{} - uniform with 'any' for interface{} proposal: Go 2: empty as an alias for struct{} Jan 16, 2023
@jimsnab
Copy link
Author

jimsnab commented Jan 17, 2023

I suspect that interface{} is used a lot more often than struct{}.

Well sure, but map[any]struct{} is a ubiquitous set.

interface{} was an overloaded concept for "anything", and struct{} was and is an overloaded concept for "nothing". One was fixed...

Anyway it would just be nice. It took me a little while to discover struct{} would be a better 'set' value than, say, a useless bool or other dummy value.

@bcmills
Copy link
Contributor

bcmills commented Jan 17, 2023

The conventional name for this type in other languages is unit, not empty.

@DeedleFake
Copy link

I suspect that interface{} is used a lot more often than struct{}.

And I expect that the usage will go down as more generic implementation of things, such as sets, are created and added to various places. The most common direct usage will probably be in chan struct{}s.

@jimsnab
Copy link
Author

jimsnab commented Jan 17, 2023

The frequency-of-use argument is orthogonal. I have more struct{}{} in my code than goto.

@ianlancetaylor
Copy link
Contributor

ianlancetaylor commented Jan 17, 2023

I don't think the frequency argument is unimportant. We could introduce all kinds of predeclared names. Each one is a tax on everyone in the Go community, because they have to learn it and remember what it means. Therefore, the bias should always be against adding a new name. They should only be added when they bring a significant benefit. An example of such a benefit would be that it makes a lot of existing code easier to read.

@jimsnab
Copy link
Author

jimsnab commented Jan 17, 2023

Nicely put.

These are all good points:

  • How a new keyword impacts existing code
  • How much code will it improve
  • Will it make the language easier to learn

I don't think the answer to these has anything to do with how much another language construct is used.

An empty struct declaration is an unexpected way to declare an empty value, which in my mind has nothing to do with structs. Enough said, it was fun to think about this morning...

@DeedleFake
Copy link

How a new keyword impacts existing code

This is actually a non-issue here. unit or empty wouldn't be a new keyword, but a predeclared identifier, which means it can be shadowed and is thus 100% backwards compatible. That's why adding any wasn't a problem, either.

@Pat3ickI

This comment was marked as off-topic.

@jimsnab

This comment was marked as off-topic.

@seankhliao

This comment was marked as off-topic.

@jimsnab

This comment was marked as off-topic.

@zephyrtronium

This comment was marked as off-topic.

@jimsnab

This comment was marked as off-topic.

@arp242
Copy link

arp242 commented Jan 22, 2023

Some rough counts on my code code, Go stdlib, and contents of my module cache:

rg -F 'struct{}'                          6,572
rg -F 'interface{}'                      19,259
rg -g '*.go' '(\bany\{\b|, any|any,)'     3,973

It seems interface{} is about 4 times as common, give or take a bit. Not fully accurate, but seems about right and should be roughly in the right ballpark.

Is that enough to warrant adding a new predeclared identifier? I don't know. But I did have to explain what this odd "struct{}{}" thing means to a few junior devs over the years; it's kind of non-obvious, and IMHO rather ugly. On the other hand, you'd also have to explain what empty or unit means (especially unit is rather non-obvious).

@DeedleFake
Copy link

especially unit is rather non-obvious

unit would be obvious to Kotlin developers, as that's what it's called in there, though it also doubles as Kotlin's version of void. I haven't seen many equivalents in many other languages, though. I think Rust usually uses an empty tuple, (), to represent an empty type. Dart uses void itself, such as Future<void>.

If something like this is considered, a corollary that I'm not entirely sure about myself could be to allow a type name to be used directly as its own zero value. That would mean that var v unit = unit would be legal, as would v := time.Time. Those are all illegal right now because type names can't be used as values, not including the exceptions of make() and new().

@go101
Copy link

go101 commented Jan 22, 2023

I believe in most struct{} use cases, people just need a zero-size type.
They don't care about the type is struct{} or [0]any.
So maybe it is better to call it zerosize?

[edit]: maybe zerocomp (means zero compisite) is better, so that zerocomp{} denotes the zero value.

@DeedleFake
Copy link

I think the opposite. The fact that it has no size is a minor optimization detail. I think most people are more interested in the fact that it contains no useful information.

@go101
Copy link

go101 commented Jan 23, 2023

struct{} is a struct kind, that is a piece of information. ;)

Maybe

// var m map[int]_
m := map[int]_{}
m[0] = _

is a better choice by this logic?

@ianlancetaylor
Copy link
Contributor

It's easy to write type empty = struct{}. We see in the conversation above that the name empty is confusing--some people think of it as analogous to nil rather than being a specific struct type.

The existing name any is different, as empty interfaces are widely used, and their use as a generic constraint is compelling.

Also the emoji voting is not in favor.

Therefore, this is a likely decline. Leaving open for four weeks for final comments.

@jimsnab
Copy link
Author

jimsnab commented Feb 2, 2023

The hope was to drive it to a standard, and not have type unit = struct{} here and type nothing = struct{} there. (Ok who's doing that, probably no one. Issue to me is largely just learning about messy and unclear struct{}{} and accepting it.)

Yes "any" is different, although... type any = interface{} was a way to go.

Likely decline due to emoji voting makes sense.

@ianlancetaylor
Copy link
Contributor

No change in consensus.

@ianlancetaylor ianlancetaylor closed this as not planned Won't fix, can't repro, duplicate, stale Mar 1, 2023
@golang golang locked and limited conversation to collaborators Feb 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests