4 ms·
IMO 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 implementin
by terminalcommand 9y ago
IMO 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.
- terminalcommand 9y agoThat's a clever way to solve the issue. I think most Go codebases could benefit from such explicit checks where the language does not provide an unambiguous solution. The interface issue has been on my mind for quite sometime. I had thought maybe declaring all interfaces in seperate files might help with the structure (like having the definitions in a header file in C), but the Go standard library does not use this approach. Interfaces and implementations are scattered across files and modules. Maybe I'm being too clever and we all know that Rob Pike always says that programmers should not be too clever. Maybe I should loosen up a bit, and treat Golang more like a pragmatic language. They did the best they could and produced a wonderful language. Any additional feature such as generics and explicit implements statements would have made the language much more complex.
- infogulch 9y agoThis can go even further. All the go files in a directory have to declare the same package name (`package mypkg`), with one exception: a test file (filename ends with `_test.go`) that has a package name that's the same as the normal package but ends with `_test` (as in `package mypkg_test`) is allowed to be in the same directory. These are actually completely separate packages and you have to import the main package if you want to actually use it. But when you run `go test` it automatically complies and runs tests in both packages. This means that you can import both your package and a dependent package to make sure interfaces are satisfied without any circular dependencies.
- cube2222 9y agoI actually experienced the opposite. Interfaces in go work so well, partly because you can create an interface which automatically gets satisfied by existing structures. For example I often use a requestDoer interface which only has the http clients "Do" method. I'm using Goland so I have jump to interface from any structure implementing it. So that's another one that gets solved by tooling. The structurally typed interfaces are actually my favorite part about Go probably.
- terminalcommand 9y agoHow do you jump to the interface from any structure implementing it? Beside from comments there is no immediate way to which interface/interfaces a structure is implementing. Maybe Gogland solves this by keeping records on all interfaces, and checking all structures whether they satisfy them. If that's the case, this is definitely solved by tooling.
- cube2222 9y agoThat's exactly what it does as far as I know. EDIT: I can also search for any interface with a struct selected, and generated stubs for all method. Well, basically what you get for any other language when implementing interfaces.
- sythe2o0 9y agoIf you need this, you can write type assertion lines in a var block of the interfaces that you explicitly want to implement (and this will throw compile time errors if they don't).
- deleted 9y ago[deleted]
- deleted 9y ago[deleted]
- slrz 9y agoThe standard godoc documentation browser does this too when started with the -analysis option. They all use the same source code oracle functionality available from the x/tools repo.
- dualogy 9y ago> I think that is so because Go hates circular imports. Well it could be but I don't think so --- "duck typing" when it comes to interface{}s has been a goal from early on, quite simply. The very idea of interfaces as "a set of func signatures" preclude the necessity to verbosely spell out "struct Foo implements BinaryMarshaler" and/or "func (_ Foo) MarshalBinary implements BinaryMarshaler.MarshalBinary"! In the same vain, you don't even need to declare interface TYPES, you can accept/pass/return "inlined" interface{MethodSig(argtype)rettype} anywhere if you want. Not the most ergonomic choice in practice, but the possibility hints at this underlying interfaces-are-duck-typed-method-sets paradigm at work here.
- dansouzaio 9y ago> And as there is no way to immediately see where some interface is declared and if/which interface is implemented you can use go-oracle (https://camo.githubusercontent.com/3fb1d62bbc7b1306da783f395c3589c3ae709b1c/68747470733a2f2f692e67697068792e636f6d2f785469546e64444856334765497936614e612e676966)for https://camo.githubusercontent.com/3fb1d62bbc7b1306da783f395... that!
- geoka9 9y agoIt's now called Go Guru: https://docs.google.com/document/d/1_Y9xCEMj5S-7rv2ooHpZNH15JgRT5iM742gJkw5LtmQ/edit https://docs.google.com/document/d/1_Y9xCEMj5S-7rv2ooHpZNH15...