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: spec: static field (and type) references #7112
Comments
Any of these options would make Go a far more dynamic language, and as such they are unlikely to be implemented. If you want to seriously propose them you need to consider how they behave for every type and every expression, not just structs and fields of structs. Labels changed: added repo-main, release-none, languagechange. |
Comment 2 by rasmus@mindplay.dk: > Any of these options would make Go a far more dynamic language I can't be sure what precisely "dynamic" means to a language expert, but I don't see the second option making the language more or less dynamic than it is already? You can already obtain both types and fields "by name", as follows: type MyType struct { MyField string } t := reflect.TypeOf((*MyType)(nil)).Elem() // 'MyType f, _ := t.FieldByName("MyField") // 'MyType.MyField It "works" - it's just cumbersome and error-prone. But in a nutshell, that is all I'm proposing - syntactic sugar for the same thing, and some lightweight, built-in types to represent those. I'm not asking for something that supports every type and every expression - in other words, not asking for C# or LINQ, just for a type-safe way to reference what can only be referenced as strings right now. > you need to consider how they behave for every type and every expression Fields really are the only problem - because structs are sets, and the current facilities only provide (type safe) direct access to the root of those sets, not to their elements; that is really the only shortcoming I'm trying to address. This can all be statically checked and resolved at compile-time - I'm not asking you to index all types in-memory by name, I realize that would change the dynamic of the language entirely. |
> I'm not asking for something that supports every type and every expression Understood, but Go is by design a language that uses a relatively small number of orthogonal constructs. We are not going to introduce a new language feature and say "but you can only use it for structs and struct fields." |
Comment 4 by rasmus@mindplay.dk: > We are not going to introduce a new language feature and say "but you can only use it for structs and struct fields." I guess I don't understand how else this feature would be useful or applicable to anything else - you can already reflect on structs (and other types) with compile-time checking, using e.g. reflect.TypeOf(MyType{}) ... so you don't need another way to solve that problem. To turn your argument around, why isn't that language feature already available for fields the same as it is for structs? Or what's missing, or what went wrong, that the existing feature don't "work" for fields? In one sense, if MyType is a type of the kind struct, then MyType.MyField is a type of the kind field; a field is a member of a set of fields in a type of kind struct, but it is also it's own thing. Field values attach and separate from their parent structs naturally and easily - but field "types" do not willing separate from their parent struct and are unreachable without them. I'm trying to view this from a conceptual point of view rather than from a technical point of view - I realize the technical side of it is more complicated than that, but conceptually, it seems somewhat incomplete when viewed that way... where by "complete", I mean fields, not just their values, are a tangible thing that you can refer to, the same way you refer to a type. I guess, where else, or how, or in what way do you picture this feature would be applicable? Or are you saying it's not, and therefore is not useful or general enough to be justified? (if so, I guess I would argue it's not so much a new feature, and perhaps in a sense more the completion of another existing feature...) |
> I guess I don't understand how else this feature would be useful or applicable to anything else All of your examples add new syntax. There is no obvious reason why that syntax should not work on types that are not structs. To me the argument that the syntax is not useful on other types is a reason to not adopt the syntax, since it is not orthogonal. But if we do adopt the syntax, it has to do something. It would make no sense to me to say that 'Foo is an error if Foo is not a struct type; what would be the basis for that? > In one sense, if MyType is a type of the kind struct, then MyType.MyField is a type of the kind field OK, sure. But the language supports naming types, and it does so for a good reason: you can declare variables with the named type. The language does not support naming fields (in the sense that you mean). That's because there isn't much you can do with a named field. For example, you can not declare a variable as having the type "field". As far as I can tell, you have a very specific use case: you want to obtain a value of type reflect.StructField from something other than the existing mechanisms. In order to do this you are proposing quite broad ranging modifications to the language, but you aren't following through on your proposal, you are just saying "there is this new way to name dynamic language constructs, it's built into the language instead of calling the reflect package, but you can only use it for struct fields." A struct field fundamentally requires two concepts: a struct and a field. You could write a function that does what you want today by taking two arguments, a struct and a field (p := &S{}; f := Field(p, &p.F)). Or you could do it using the existing language feature unsafe.Offsetof (f := Field(S{}, unsafe.Offsetof(S{}.F)). I agree that these are not especially pretty, but if you want pretty it needs to be orthogonal and clean, not just specific for struct fields. |
So I stumbled on this issue looking to see if something like this functionality existed (or had been proposed). The specific use case which prompted me to think about it is that it would be nice to be able to do something like the following: import "time"
type StructWhichMaintainsState struct {
IPAddress string
Name string
NumberOfThings int
LastUpdate time.Time
}
// In this function definition, channel "ch" is defined as a field type on
// StructWhichMaintainsState. The enforcement here is that whatever you put into
// this channel must be cast to the same type as the struct field.
func ipUpdater(name string, ch chan StructWhichMaintainsState.IPAddress) {
for {
ipAddr := doSomethingWhichGetsTheIP(name)
castedStringToFieldUpdate := StructWhichMaintainsState.IPAddress(ipAddr)
ch <- castedStringToFieldUpdate
time.Sleep(time.Second)
}
}
func stateManager(state *StructWhichMaintainsState) {
// Channel carrying any data type
updateCh := chan interface{}
// This cast should probably be optional? But its here for c
go ipUpdater(state.Name, (chan StructWhichMaintainsState.IPAddress)updateCh)
// The real power is here: the type switch here lets us select between
// multiple incoming "field" types off the one channel - removing the need
// for definiting separate struct types for communicating these types of
// "patch" updates to a struct.
for incomingUpdates := range updateCh {
switch v := incomingUpdates.(type) {
// We could do something like this...
case StructWhichMaintainsState.IPAddress:
state.IPAddress = v
// But for real convenience we would instead like to just do this:
case state = <- v:
// This syntax says "apply the value of v to a matching field type
// within the target struct. Since a field type matches *exactly*
// that named field, this is a predictable shorthand.
}
state.LastUpdate = time.Now()
}
}
func main() {
// Some big map we need to lookup infrequently, which holds a lot of structs
// of variable state.
mapOfState := make(map[string]*StructWhichMaintainsState)
// We make some item which will receive updates here and launch a goroutine
// to keep updating it.
stateMan := &StructWhichMaintainsState{Name: "name1"}
go stateManager(stateMan)
mapOfState["name1"] = stateMan
stateMan := &StructWhichMaintainsState{Name: "name2"}
go stateManager(stateMan)
mapOfState["name2"] = stateMan
} The syntax isn't dead on, but the concept is that a "named field type" allows going from an |
We aren't going to be making this kind of change. It might be possible to discuss some sort of Closing this issue. |
Yeah this sucks. For metaprogramming etc, we need to tag structs with meta-info. Static fields are perfect for that. Until then, I think you can store a struct in a map as the key, and put your static fields as the values in the map? |
by rasmus@mindplay.dk:
The text was updated successfully, but these errors were encountered: