5 ms·
> my intuition what I think a given piece of code should mean is nearly always in line with the language specification. for some things yes, but for others I t
by tetraodonpuffer 9y ago
> my intuition what I think a given piece of code should mean is nearly always in line with the language specification.
for some things yes, but for others I think that it's more familiarity than intuition, take for example interface slices, you start by learning that you can assign any type to interface{}, so intuitively you'd think that you could assign any type slice to []interface{} but you find out soon enough that doesn't work
If you dig a bit it is completely understandable why it doesn't given how interfaces are laid out in memory, but from an intuitive standpoint you'd think that since you can do it in the scalar case you should be able to do it in the slice case too...
This is a very minor nitpick, I do agree that compared to many other languages golang is very easy to mentally parse, at the expense of some expressivity at times.
Whenever I code in it, which these days is pretty much all the time, I do miss some conveniences from other languages (generics, list comprehensions, ...) but of course every language has its tradeoffs, I most certainly miss golang's strengths every time I have to write some C or python.
- terminalcommand 9y agoIMO interfaces are the Achilles' heel of Golang. Another thing that has been bugging me about them is that you cannot specify which interface you're implementing in the code (beside from comments). I think that is so because Go hates circular imports. If you had to import a file to be able to use an interface, you'd soon run into compile errors due to circular dependencies. And as there is no way to immediately see where some interface is declared and if/which interface is implemented, you have to rely on comments and resort to manual search to find out more. That may be easy in smaller codebases or worthy of effort in important codebases such as Go standard library. But all an all this ambiguity does not fit with Go's discipline to prevent human errors by making everything uniform and explicit as possible. Just for the sake of providing an example, let's look at time.go under https://golang.org/src/time/time.go https://golang.org/src/time/time.go, we can see in the comments of several functions that the programmer states a particular interface is implemented. Line 1112 of the aforementioned file is as follows: // MarshalBinary implements the encoding.BinaryMarshaler interface. func (t Time) MarshalBinary() ([]byte, error) { Now where is encoding.BinaryMarshaler declared? There is a package called encoding, maybe there? But the package has many many files, where would I find where BinaryMarshaler is defined? I'd have to resort to manual search. Now imagine that this is not an interface in the standard library, but rather an interface in some mediocre codebase that you're handed for the first time... Tl;dr All I'm saying is that runtime errors due to empty interfaces are not the only flaw of Golang. Interfaces as implemented in Go could in some cases present a serious threat to code structure.
- krylon 9y agoIt would be nice to add at least the options to explicitly state that type X should implement interface I, and have the compiler barf / emit an error if X does not implement Y. Like I said before, this has not been enough of a problem to really bother me, but I would be quite happy if Go fixed this.
- tetraodonpuffer 9y agocan't you simply do this (from [1]) somewhere in your code to enforce? type T struct{} var _ I = T{} // Verify that T implements I. var _ I = (*T)(nil) // Verify that *T implements I. [1] https://golang.org/doc/faq#guarantee_satisfies_interface https://golang.org/doc/faq#guarantee_satisfies_interface
- terminalcommand 9y agoYes you could, but then you'd have to have both the declaration and the implementation of the interface in the same package. That would mean that if either of them is in a separate file, you'd have to import it. That in return, could easily call hellish compiler errors due to circular dependencies. In Go any circular dependency is an immediate compile error. Looking at the link, the second advice is that you could require users to implement special functions such as ImplementsFooer() to explicitly declare which interface they're implementing. This could help, but this practice is not present in the codebases I've seen including the Go standard library (to the best of my knowledge).
- majewsky 9y agoA possible solution is to have a separate (otherwise unused) package in your codebase with just a test in it: //compile_test.go func TestThatThisModuleCompiles(t* testing.T) {} //list of "type implements interface" tests var _ mymodule.MyInterface = myothermodule.MyStruct{} ... Then you just ensure that `go test` is run, e.g. by the CI.
- donatj 9y ago> intuitively you'd think that you could assign any type slice to []interface{} but you find out soon enough that doesn't work I've been writing Go for upwards of six years, professionally for three, and I have never tried that. I can't think of the last time I used interface{}... I generally consider anything using interface{} crap code someone who doesn't know the language wrote, probably coming from a loosely typed language.
- dualogy 9y ago> I generally consider anything using interface{} crap code someone who doesn't know the language wrote, probably coming from a loosely typed language. Disagree. There's ample usage of interface{} that you need to type-switch on in parts of the stdlib that deal with the AST and reflection or various other areas. You might also UnmarshalJSON a structure where some field can contain any one of a number of possible sub-structures. Once we're talking about 1-2 dozens of possible sub-structures, you'll have that field be an interface{} and type-switch on it --- rather than having 1-2 dozen pointer fields that will all-but-1 be nil and having to if-else through them all.
- slrz 9y ago> take for example interface slices, you start by learning that you can assign any type to interface{}, so intuitively you'd think that you could assign any type slice to []interface{} but you find out soon enough that doesn't work Why do you expect this to be the case? Just because you can assign a char to an int in C, you wouldn't expect to be able to assign a char pointer to an int pointer. Similar for C++ and std::vector<char> vs std::vector<int>. It's the same and probably even more relevant with void∗/int∗ (assignable) vs. void∗∗/int∗∗ (not assignable). You can assign an int∗∗ to a void∗, though, just as you can assign an []int to an interface{} in Go. Also, even as an absolute greenfield programmer with no experience in other system languages, if you really think it through, you will come to the conclusion that it mustn't be possible to assign arbitrary slice values to an []interface{} variable, given the way slices work.