5 ms·
With Go, I use a decent amount of generated code to create repetitive bits of code that only have small variations (e.g., the type!). Most of this code seems t
by Winsaucerer 4y ago
With Go, I use a decent amount of generated code to create repetitive bits of code that only have small variations (e.g., the type!). Most of this code seems to not be possible for me to replace with generics yet, so I'm still finding myself using generated code. For example, a bunch of structs may share a field, but Go generics don't yet support accessing a field that is shared by types accepted by the interface.
- xyzzyz 4y ago> For example, a bunch of structs may share a field, but Go generics don't yet support accessing a field that is shared by types accepted by the interface. You can easily and cleanly work around this by exposing getter and setter method in your interface.
- tapirl 4y agobut is it cleanly? BTW, accessing common fields is temporarily disabled before releasing Go 1.18, because there is another fundamental problem which needs to be resolved firstly. ref: https://github.com/golang/go/issues/51259 https://github.com/golang/go/issues/51259
- deleted 4y ago[deleted]
- throwaway894345 4y agoRight. It’s definitely not “clean” IMO.
- xyzzyz 4y agoWhich other language has interfaces specifying field access, that does not go through indirect method call anyway? I guess if you consider C++ templates to be interfaces, that would apply, but C++ templates are even less clean (given that there isn’t even any clean way to specify interface expected by C++ template, all you have is type traits, which are opposite of clean).
- siknad 4y ago> given that there isn’t even any clean way to specify interface expected by C++ template, all you have is type traits Concepts?
- xyzzyz 4y agoAh, sorry, I was not up to speed with C++, it’s called concepts now. So, how do I specify, using concepts, that my template expects a type which has a field “.foo” of type int?
- siknad 4y agotemplate<typename T> concept has_foo = requires(T x){ { x.foo } -> std::same_as<int>; }; template<has_foo T> int get_foo(T x){...} // or template<typename T> requires has_foo<T> void f(T x){...} // or even void f(has_foo auto x) {...}
- BobbyJo 4y agoDoes the above provide more safety/guarantees/something than just: type hasFoo interface { getfoo() int setfoo(int) } func f(hasFoo) {} ?
- BenFrantzDale 4y agoI don’t know Go, but I think less in that concepts are syntax guarantees, not semantic guarantees. I suspect interfaces (assuming they are opt-in) are semantic guarantees. That said, I’ve never run into this distinction being a problem, and you could provide opt-in mechanisms in C++. Of course, the C++ interface has zero runtime cost.
- throwaway894345 4y agoThe equivalent Go would be something like: func f[T interface{.Foo}](T) {} Of course, this doesn't exist in Go, so we must do: func f[T interface{GetFoo(); SetFoo()}](T) {} And then define setters and getters on every type we want to use, including defining new types (with the commensurate setters/getters) for types that we don't own (i.e., if you import a struct from another package and it doesn't have setters and getters defined, you have to create a new type that implements those setters/getters).
- Winsaucerer 4y agoYeah, you can, but then you need to expose those fields for a whole bunch of similar types. If I took that approach, I'd use generated code to reduce the repetition, and still in the same boat -- I'm either generating the functions for each type, or generating the getters/setters for each type. Rust is the same I think, where you'd need to have methods that explicitly implement the trait (interface in Go). However, Rust has a built-in code generation tool (macros), and you can decorate your structs to have it automatically implement some traits for you. That's the equivalent of adding one more entry to your Go generated code loop, except that it's right next to the struct. If Go did support field methods (which it looks like they may one day), that might be the tipping point for me for disabling a bunch of generated code. Not sure if there's other things getting in my way, because I gave up once I noticed I couldn't do that.
- Groxx 4y agoI was recently thinking that I would really like "field references" in go, e.g. via type.Field, like you can do for methods to get unbound methods. Partly because it might enable generics over "contains field(s)" instead of just "contains method(s)". In the meantime I guess there's always anonymous functions (to use as accessors). They're not hard to be generic over.
- morelisp 4y agoWhat would you use such a field reference for that a https://pkg.go.dev/reflect#StructField https://pkg.go.dev/reflect#StructField isn't appropriate?
- Groxx 4y agoI haven't tried benchmarking a cached reflection-driven field reference, now that I think about it... I'll give that a try, very good point. If that performs the same as a normal field access (or the equivalent behind a non-inlined function or something), yeah, that'd be completely fine. My main thought was that you can very easily reference and use `type.Method` (you just have to pass an instance as an additional first argument), so a straightforward `type.Field` equivalent would be convenient in a number of places. E.g. you could avoid the need to add accessor methods, which isn't possible on types you don't control, and no need to create anonymous functions everywhere to work around that.
- morelisp 4y agoIt definitely won't match the performance of normal field access, just as passing an unbound method reference won't be as fast as calling it directly, or calling through an interface won't be as fast as calling on a concrete type. The best performance you'll get is probably from putting the fields together into a substructure and passing around pointers to that. Which doesn't need any reflecting or additional language scaffolding.
- Groxx 4y ago