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: add some more 'smarts' to literals of types using empty struct{} #23915

Closed
splace opened this issue Feb 18, 2018 · 25 comments
Closed
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@splace
Copy link

splace commented Feb 18, 2018

I'm not sure if this doesn't have some nasty drawback, but I have thought about it several times and would like to share it for your consideration.

Basically its about being able to miss out typing 'struct{}' in literals where its unambiguous.
(i did try making a value and naming it, say 'Nothing := struct{}{} ', but that’s still not great.)

Case 1

a few times, i have composed with types that have no state themselves, say; they implement some default static behaviour to meet an interface.

type Opaque struct{}

func (o Opaque) isOpaque() bool {
	return true
}

currently, when made literally, you need to specify the empty struct, which seems a bit redundant/unclear, or use named parameters which often results in stuttering.

type OpaqueColor struct{
        Color
        Opaque
}

c:=OpaqueColor{Color{},struct{}}

or

c:=OpaqueColor{Color:Color{}}

clearer?

c:=OpaqueColor{Color{}}

imagine if you had several empty types.

case 2.

when you use a map with empty structs values, say to make a set, the literal can get a bit bloated;
(code below cut/paste from a web search.)

cc := &dockerclient.ContainerConfig{
    // ...
    Volumes: map[string]struct{}{
        "foo": struct{}{},
        "bar": struct{}{},
   },
}

clearer?

cc := &dockerclient.ContainerConfig{
    // ...
    Volumes: map[string]{"foo","bar"},
}

also, if you change to/from a slice of strings its trivial to update the literal.

case 3

using channels of empty struct{}

done := make(chan struct{})
// ...
done <- struct{}{}

i think this is neater, since empty struct types are all the same anyway

done <- {}
@gopherbot gopherbot added this to the Proposal milestone Feb 18, 2018
@davecheney
Copy link
Contributor

In case 1, if you have several differently named empty types, their zero values is identical to their initialised value, so you can initialise them, or not initialise them and the results is the same.

Case 2 and 3. Remember that go optimises for the reader by reducing the number of different ways to represent the same syntax. I'd suggest either

var stop struct{}
done <- stop

or

done := make(chan int)
done <- 0

as an unbuffered channel of an int and an unbuffered channel of a struct{} consume the same amount of space.

@pciet
Copy link
Contributor

pciet commented Feb 19, 2018

These cases are in my mind not taking full advantage of Go.

  • empty struct types to satisfy an interface indicate that the interface isn’t designed well
  • sets are better represented as a slice
  • for readability type Signal struct{} could be used

@splace
Copy link
Author

splace commented Feb 19, 2018

In case 1, if you have several differently named empty types, their zero values is identical to their initialised value, so you can initialise them, or not initialise them and the results is the same.

so;

c:=OpaqueColor{Color{},Opaque{}}

or

var o Opaque
c:=OpaqueColor{Color{},o}

@splace
Copy link
Author

splace commented Feb 19, 2018

as an unbuffered channel of an int and an unbuffered channel of a struct{} consume the same amount of space.

where is the int's value held then?

@splace
Copy link
Author

splace commented Feb 19, 2018

sets are better represented as a slice

i think of sets as something you cant have duplicates in, because that is what you are representing.

@davecheney
Copy link
Contributor

Neither,

var c OpaqueColor

@splace
Copy link
Author

splace commented Feb 19, 2018

Neither,

var c OpaqueColor

the empty Color type is just to save me typing, assume it wouldn't always be empty. this is about literals.

@davecheney
Copy link
Contributor

the empty Color type is just to save me typing, assume it wouldn't always be empty

There is no value (ha!) in explicitly initialising a field with an underlying type of struct{}. You don't need to do it, so it shouldn't be necessary to change the language to make that operation less verbose.

@jimmyfrasche
Copy link
Member

cases 2 and 3 would be handled by #12854 which is more general

@splace
Copy link
Author

splace commented Feb 19, 2018

There is no value (ha!) in explicitly initialising a field with an underlying type of struct{}. You don't need to do it, so it shouldn't be necessary to change the language to make that operation less verbose.

reply confuses me;

assume in all previous examples Color has a non-default value, say Color{1,2,3} or something

c:=OpaqueColor{Color{1,2,3},struct{}}

or

c:=OpaqueColor{Color:Color{1,2,3}}

@splace
Copy link
Author

splace commented Feb 19, 2018

cases 2 and 3 would be handled by #12854 which is more general

very close, it think it would leave the colon and empty braces in 2, but sure near enough.

@davecheney
Copy link
Contributor

@splace you don't need to initialise fields with an underlying type of struct{}, there is no way you can initialise it in a way that is distinguishable from the field's zero value.

@splace
Copy link
Author

splace commented Feb 19, 2018

@davecheney which is what i'm looking for, example?

@davecheney
Copy link
Contributor

@splace see your example here #23915 (comment)

@splace
Copy link
Author

splace commented Feb 19, 2018

@davecheney that is an example showing whats bad? where you DO need to initialise fields with an underlying type of struct{}

@davecheney
Copy link
Contributor

where you DO need to initialise fields with an underlying type of struct{}

I don't. They don't need it.

@splace
Copy link
Author

splace commented Feb 19, 2018

@davecheney
Copy link
Contributor

I think we're talking past each other. Your problem appears to be when there is more than one named field in a struct, then you need to explicitly name the fields when using a compact initialiser. I think this is unrelated to empty structs.

https://play.golang.org/p/rrXJJA01bya

@splace
Copy link
Author

splace commented Feb 19, 2018

@davecheney

your problem appears to be when there is more than one named field in a struct,

my examples are all with unnamed fields!

I think this is unrelated to empty structs.

it is completely and ONLY related to having to type out empty structs when there is enough info to infer them.

@davecheney
Copy link
Contributor

davecheney commented Feb 19, 2018

my examples are all with unnamed fields!

I respectfully disagree, as I showed in my previous example. Your example's field has a name, it's name is Opaque. In Go a type may be named, or unnamed, however every field in a Go struct has a name

type Q struct {}

type T struct {
    A int // this field's name is A
    B struct{} // this field's name is B
    Q // this field's name is Q
    q Q // this field's name is q
}

@splace
Copy link
Author

splace commented Feb 19, 2018

sorry, i thought these were called anonymous fields.

(edit)
with some research i see a fair bit of use of the term anonymous fields in Go, but only from way back, unfortunately still many web pages, there was a mostly consistent naming convention fixed 5 years ago, which is clearly better. i must try to re-fix that in my memory.

@splace
Copy link
Author

splace commented Feb 19, 2018

but the point is being lost; as mentioned before #12854 is the same thing, only more general, applying as it does to other than just empty struct fields. personally i find the empty structs most significant conceptually and visually and would probably favour being explicit, over implicit, for other than them

@dsnet dsnet added v2 A language change or incompatible library change LanguageChange labels Feb 19, 2018
@creker
Copy link

creker commented Feb 19, 2018

Looks to me like a more specialized version of #19642 struct{}{} is annoying to type and makes your code ugly. But I think if something is to be done in that regard it should cover all cases rather than introduce another special-case syntax.

@pciet
Copy link
Contributor

pciet commented Feb 19, 2018

i think of sets as something you cant have duplicates in, because that is what you are representing.

For the container configuration volume set case using a map causes a compiler error if there are duplicate items. My mistake, a slice would not be better here.

There is already a shortcut though:

Volumes: map[string]struct{}{
    "foo": {},
    "bar": {},
},

Using a type for map[string]struct{} is another option although that loses the compiler error for duplicates: https://play.golang.org/p/kkB7bnwkhCe

@ianlancetaylor
Copy link
Contributor

This proposal adds syntax to support the relatively rare situation of an empty struct; if it occurs frequently in some code there is the easy workaround of introducing a variable. #12854 and #19642 are more general approaches in this area. Closing this issue in favor of those.

@golang golang locked and limited conversation to collaborators Apr 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

8 participants