Hacker News new | past | comments | ask | show | jobs | submit login
Some Tools for Go Lang That You Might Not Know Yet (gopheracademy.com)
129 points by type0 on Dec 11, 2016 | hide | past | favorite | 26 comments

I've used go-bindata[1] for embedding things like shell scripts and data files into my applications, it's a fantastic tool!

[1] https://github.com/jteeuwen/go-bindata

I use go-resource[1], it works nicely for embedding websites as well as scripts and other resources.

[1] https://github.com/omeid/go-resources

The tool I have found very useful is goimports https://godoc.org/golang.org/x/tools/cmd/goimports really great for automatically adding and removing imports.

Really? Interfaces everywhere you can possibly imagine? Is that where we're heading in Go land?

> Really? Interfaces everywhere you can possibly imagine? Is that where we're heading in Go land?

That should be where you are heading, because contracts should be separated from implementation, things should not be tightly coupled, etc. This is good programming practice. How can you be opposed to accepting interfaces instead of implementations, especially in a standard library or the standard tooling for the language?

Accepting interfaces is an implicit commitment to support external implementations of the interface. You can't add a method to an interface without breaking clients who do this. This makes interfaces more fragile than structs.

A concrete example: If I have a NewFoo function that returns a Foo and a FooTaker function that accepts a FooInterface, I can't assume that new features I add in NewFoo are usable from FooTaker. If FooTaker just took a Foo, there would be no problems.

This is, I think, the best rule of thumb for when to use an interface: If you expect consumers of your library to write their own data types that could be used by your library, define an interface they can satisfy. Otherwise just produce and consume a struct you control -- it will be much more flexible and maintainable in the long run.

In your example, the best solution would be for NewFoo to return an implementation of FooInterface.

If "Foo" represents anything at all complex or stateful (e.g. a database connection, a model of some data, an HTTP server, etc.), and you might want to extend it later, it's going to be messy.

If you assume there is only ever going to be one implementation of "Foo", then there's nothing to be gained by using an interface over a struct with private fields. If there are other implementations, then you have to update every implementation in sync with the interface every time you add or change a method. That might be very difficult or even nigh impossible if they're in different packages maintained by different people.

My point is to be very careful with how you use interfaces. They're designed to represent abstractions that apply to multiple data types. Looking at the standard library, Reader, Writer, Stringer, http.Handler etc are all pretty useful as interfaces. database/sql/driver has the Driver interface, which is about as complicated as you should allow an interface to become. Notice that nothing in database/sql implements the Driver interface -- it's strictly for polymorphism, to consume external implementations -- and nearly every type in database/sql is a raw struct.

Reading interface heavy code is harder because interfaces are another layer of abstraction

That's not an interface everywhere you can imagine, that's a huge, bulky interface that should probably be many smaller interfaces. No one is arguing for 16-method interfaces when all you want to do is call s.Alert().

Your point?

For libraries, accepting interfaces (not interface{} but a useful interface with expected methods) seems to be better than accepting a specific type. For 'main' programs, I don't find this to be true.

Do you have concerns about interface arguments?

A rule of thumb I've heard and agree with for libraries is "accept interfaces, return structs".

Rationale here: https://medium.com/@cep21/what-accept-interfaces-return-stru....

The linked article doesn't elaborate on the "return structs" part. Personally I prefer to return only the bare minimal functionality that your caller should expect, so that they don't depend on something they shouldn't and you don't break them when you change to give them less. What are the best reasons to return structs?

This is also the style advocated by the standard library. See for example the helper functions for io.Reader: https://golang.org/pkg/io/#Reader

For example, `io.TeeReader` returns `io.Reader` (the interface). The underlying type that implements the tee functionality is left private because you don't need to be able to see it. It really drives home the point that the return value is "just another io.Reader".

This depends on the purpose of your function. It is the responsibility of the caller to not assume too much about dependencies, but you should help by being clear about the expected types of your functions. However that doesn't mean you should restrict yourself to always returning interfaces.

Imagine your documentation says: this functions returns a List of items. You can still returns a CopyOnWriteArrayList. Your only promise is that it will satisfy List, but if you returned the interface, you would remove the ability for the next developer, which has more information than you, to rely on it. Since you cannot know in advance who is going to use the return value, giving the most specific type is the approach that is the most composable. A private function that knows what to do with CopyOnWriteArrayList can rely on it. Other code that works on List can accept the value because it is a List.

If your interface is going to be public, then relying on the documentation might not be enough, and then you are welcome to define facade functions which hide internals.

>Imagine your documentation says: this functions returns a List of items. You can still returns a CopyOnWriteArrayList.

I don't write in Go so I don't know how the Go community feels about this, but I don't like the idea of a mismatch between documentation and implementation. A non-trivial number of people will only read the documentation when something goes wrong. If the function is defined as returning a CopyOnWriteArrayList they will assume that's the return type. If they bother to read the documentation, the mismatch will be confusing and the more obvious inference will be that the documentation is simply out of date.

>you would remove the ability for the next developer, which has more information than you, to rely on it

Yes, this is exactly what I _want_ to do. If the fact that I produce a specialised type rather than an interface is purely incidental, then I don't _want_ people to rely on it -- I won't be able to change my mind later. "Don't promise what you can't deliver." If it's not incidental, then of course be as precise as you feel is appropriate.

Also I'll concede that this opinion is mostly for library writing, less so for internal usage where the caller is you/your team anyway.

My rule of thumb is: if you don't have a simple name for your interface and you're about to call it "<your-struct-name>Interface", then you shouldn't have an interface.

He he. Soon people will collaborate on a Go EE (enterprise edition) standard, and Go will introduce optional generics and later, lambda syntax.

> Soon people will collaborate on a Go EE (enterprise edition) standard

Yeah - build your application as a Caddy plugin, then you can use their routing / ACME handling...

There are a lot of similarities. Hopefully Go developers can bypass some of the pitfalls along the journey.

Duke Gopher

I don't think this tool is meant to enforce "interfaces everywhere", but rather a tool to give you insights into your code. If a functions parameter type could be changed into an interface instead of a concrete type, you could decouple it from that type. This is nice for library functions which then could be called with types defined by the client, as long as these types implement that interface.

If you mean for `interfacer`, I think that the example there is too arbitrary, or at least I'd hope that the real world usage of this would be to analyze many thousand lines of a package or something..

Looks like Go is becoming java ;)

Well, some indeed try to sneak in what they learnt using Java into Go...

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact