
How to Use Go Interfaces - boyter
https://blog.chewxy.com/2018/03/18/golang-interfaces/
======
tepidandroid
Great article that corroborates what I've also personally seen in many Go
applications.

IMHO, if you're coding Java style interfaces up-front ("Repository",
"Service", etc) you're already doing it wrong. It's likely that you're either
over-engineering your project or prematurely optimizing for flexibility you
will not need. You'll likely feel as though you're fighting the language to
build these abstractions and layered architectures.

Go is all about minimalism. As the article says, you should be writing the
concrete implementations first, _then_ defining interfaces in the client code.
The calling code should pick and choose the behaviour it needs from the
implementation. As hard as it is for most seasoned Java developers, you have
to silence that anxious voice inside that keeps asking "what if I need to swap
this out for something else?". Go is about getting things done, not beautiful
abstractions.

~~~
seanmcdirmid
Sometimes you don’t know what the implementation will be yet, and writing a
bunch of interfaces out can help you discover what it should be, then you
start working it out with interfaces gradually giving way to implementation.

Interfaces are rarely rarely about interoperability even in Java. Most
interfaces have one implementation. Instead, they are about abstraction,
design, and so on. This article assumes that the end product is what was
intended all along.

~~~
catnaroek
> Most interfaces have one implementation. Instead, they are about
> abstraction, design, and so on.

Then what you want is an abstract data type. Unlike interfaces, which can be
implemented anywhere, an abstract data type is implemented in a single place.
This has obvious benefits for performance (clients don't need to dynamically
dispatch on operations), control (implementors don't need to worry about
malicious clients supplying impostors) and precision (a single module can
export several abstract types, whose implementations are mutually dependent).

Go's interfaces are pretty terrible at all of the above. Not that Java
provides anything substantially better.

~~~
seanmcdirmid
I have only seen abstract data types used to describe stacks and queues, and
never in the context of a larger design where the underlying implementations
weren’t data structures but objects. Do you have any good examples?

~~~
chewxy
the sort.Interface is an abstract data type. It's poorly named IMO. If it's
called sort.Collection, it'd make more sense

~~~
catnaroek
A Go interface type is not an abstract data type. The difference is not even
subtle:

(0) An ADT has a single internal representation that is known to the
implementor, but hidden from users. The upside is that the ADT implementor can
use his knowledge of the representation to optimize on n-ary operations, i.e.,
operations that act on several instances of the ADT. The downside is that ADTs
cannot be extended by third parties. You would have to create a different ADT,
even if satisfies the same contract.

(1) A Go interface has a list of methods, but does not proscribe any fixed
internal representation. The upside is that anyone can create values of an
interface type as long as they can implement the methods. The downside is that
it is difficult to implement n-ary operations, because, at any point in the
program, the implementor only knows the internal representation of the single
value they are currently creating.

In other words, ADTs optimize for efficiency and predictability, whereas
interfaces optimize for flexibility and extensibility. As a general rule, ADTs
require more time than interfaces to plan ahead their design and
implementation, but already implemented ADTs are easier to use correctly than
already implemented interfaces.

------
nothrabannosir
I’m not a big fan of contrived examples with imaginary use cases, because they
tend to fall short in actual use.

 _> “Accept interfaces, return structs”_

Real example from personal (professional) life against that: Amazon AWS SDK
defined S3 as a struct. That makes it bloody hard to mock during unit testing.
I eventually ended up defining my own “s3iface” interface with corresponding
mock to combat this, but now I have to repeat this for every program that uses
it. Ok, I’ll separate it into its own package and include that everywhere.
Wait, what was the point of the AWS SDK not doing this directly, again? Oh,
look, they eventually ended up doing just that: providing an interface version
of their S3 struct:

[https://godoc.org/github.com/aws/aws-sdk-
go/service/s3/s3ifa...](https://godoc.org/github.com/aws/aws-sdk-
go/service/s3/s3iface)

 _> Package s3iface provides an interface to enable mocking the Amazon Simple
Storage Service service client for testing your code._

With the exact same name, to boot.

Please consider providing and returning interfaces. Even if your struct is the
only type implementing it.

~~~
sethammons
My real world AWS sdk s3 experience: I have an uploader interface (I only
needed to upload). The concrete uploader has the s3 code. The fake uploader
has any custom result/err I need to test against.

~~~
yup123
No idea why you’re getting downvotes, seems like a clean way to do this.

------
JepZ
Btw. Interfaces can also be used to resolve circular dependencies: Just define
compatible interfaces in both packages et voilà, no circular dependencies
anymore.

I am not sure if I agree with

> Defining an interface upfront is usually a code smell for overengineering.

So far I had less problems with overusing interfaces than with not using them
enough. So when you write tests for a package, you define the general
interface to the package in terms of types and methods. The remaining
challenge is to break that general definition into smaller pieces as one big
interface for a package is almost certainly a bad idea.

However, in general I follow the authors verdict to not just build 'Java-
style' interfaces, as that completely misses the point of interfaces in Go.

~~~
fiatjaf
If you have circular dependencies you're doing something wrong. By disagreeing
with that and accepting circular dependencies as a normal state of the Go
code, you put yourself in the position of doing another wrongness: defining a
lot of interfaces.

~~~
sanderjd
There is some stuff I really love about Go, but the seemingly pervasive
"you're doing that wrong" culture typified by your comment is a really bad
look for a language community. It is much better to show people why things are
better than to make proclamations. Neither of the things you proclaim to be
"wrong" are wrong a priori; show us why your way is better.

~~~
lsaferite
Having recently moved into go at work and interfacing with its community, I
100% agree with this. The absolute dogma drives me crazy and makes me not want
to deal with the go community at all.

------
klmr
I don’t think the reference to Postel’s law is correct here: Postel’s law is
about _robustness_ , not about abstractness.

Furthermore, taken at face value, Postel’s law was a phenomenal failure in
principle and in practice (see e.g. [1]). In hindsight it’s probably a bad
design principle.

[1] [https://tools.ietf.org/html/draft-thomson-postel-was-
wrong-0...](https://tools.ietf.org/html/draft-thomson-postel-was-wrong-02)

------
tptacek
Go's crypto interfaces, in the standard library, don't hew to this "accept
interfaces return structs" rule. NewAES, for instance, returns a cipher.Block.

~~~
chewxy
It's in there in a footnote (click on one of the *):

> Of course there are subtleties that I am eliding. For example, context-
> sensitive code would benefit very much from taking interfaces and returning
> interfaces

Or to steal from Haskell's lexicon: when you do something in a monad.

~~~
gkya
Showerthought: wouldn't it be easier to comprehend if Haskell called monads
contexts?

~~~
chewxy
Troll answer: but then where would people go if they want to show off their
ability to talk in jargon like "applicatives", "arrow", "lenses"?

Serious answer: Not really. Monads are more abstract than just contexts.
Monads are commonly used to represent context and contextual computation, but
they can represent other things too.

~~~
gkya
And that goes to show that I haven't really understood them. I won't ask for
an explanation because most probably I won't understand them anyways :)

~~~
balefrost
I'd argue that there's no way to understand monads as a whole except by
understanding the monadic laws. That is to say, I don't think there's some
grand theory that unifies all monadic types. Rather, I think that certain
types just happen to be monadic and, as a result, we can reason about and do
certain things with those types that we can't do with other types.

What's the similarity between List, State, and IO? I'd argue that there is
little similarity apart from the fact that we can define the monadic
operations for all three. And, having defined those operations, we can then
use more complex operations like `sequence`.

Monadic types sometimes represent data structures, sometimes represent
contexts or, in the case of IO, sometimes represent nothing really at all. IO
is arguably just a marker that imposes a linear sequence on what would
otherwise be unsequenced computation.

In short: don't spend too much time trying to understand monads as a whole.
Rather, try to understand why specific types, like List and State, happen to
be monadic.

------
kasey_junk
If your package itself defines multiple implementations (or you _know_ of
multiple packages that do) feel free to preemptively define/return interfaces.

Other than that interface definition should be done at the call site, not the
implementation site. This is true in Java also. Golangs structural typing
doesn’t change that, it just removes boilerplate in the most trivial cases.

~~~
zzbzq
This was so absurd to me at first I had to think about it for a while.

I guess you wrote it as a continuation of the opinion from the article that
interfaces should only be used as input.

From my perspective, I was still considering all other cases also. For other
cases, it's precisely because of Go's structural typing that such a thing can
even be considered. Java can't implement an interface for a type it doesn't
control. You could create a wrapper class, but not exhaustively wrap all
present and future implementations.

~~~
kasey_junk
The only reason to have an interface is to decouple the use from the
implementation (other than some fairly specific reasons that the article
mentions like sealed interfaces).

To decouple the use from the implementation we generally are either adapting
the implementation or narrowing it. In the latter case, structural typing
saves you boilerplate, in the former it likely doesn't.

The OOP consultant set calls the the concept we are dancing around Dependency
Inversion. In Java it very routinely results in wrapper classes. In the most
basic cases those classes are pass through boilerplate, but those are fairly
rare in practice. Golang dismisses with those.

In more cases in practice, you are actually defining the interface that
matches the call site demands and modeling different implementations to that.
Golang's structural typing doesn't help much with that.

I tried (not terribly successfully) to write up some of this in the DIP
section of this [http://kc.my-junk.info/di-ioc-dip](http://kc.my-junk.info/di-
ioc-dip) it uses scala for the code samples, but it should be fairly easy to
see that golang doesn't remove much of the wrapper code if I'd used that.

------
keypusher
I don't see what the benefit is of switching it around like this. The author
says the problems with the original implementation are:

> The most obvious of which is that it has only one type that fulfills the
> interface with no obvious means of extension. (sic)

Seems obvious to me.

type Cat struct{}

func (a Cat) Speaks() string { return "meow" }

Does this not work?

> Functions typically take concrete types instead of interface types.

Ok...? This seems like some arbitrary rule that the creators of Go claimed was
a good idea, I guess. The author has offered no justification for it. In
general I guess I fail to see most of the proclaimed benefits of Go, every
time I read articles like this I come away favoring something like Java for
anything in the application layer. I can see preferring Go to C for low-level
and systems programming, but I shudder to think of what a mess the large Go
codebases must look like.

~~~
chewxy
Author here.

> Does this not work?

It works

However, this is what I have recently found quite a lot. An interface is
defined:

    
    
        type Foo interface {
            Method1()
            unexportedMethod()
        }
    

And in the same package, only one type (usually unexported) implements the
interface. And the functions declared in the package typically take the
concrete type instead of the interface type.

This is typical when developers come from Java and they do `abstract class
Fooer` and immediately do `class Foo implements Fooer` within the same
package. This is an anti pattern in Go.

> This seems like some arbitrary rule that the creators of Go claimed was a
> good idea, I guess

No. I think I was unclear. I specifically mention that it's not a great idea.
It's a code smell. Instead, interfaces should be defined at point-of-use.

~~~
keypusher
> I specifically mention that it's not a great idea. It's a code smell.
> Instead, interfaces should be defined at point-of-use.

You still have not said WHY it's not a great idea, or what the benefits of
defining the interfaces at point-of-use are.

~~~
chewxy
The benefits of defining interfaces at point of use is decoupling of
dependencies.

Imagine you have two packages: A and B. A defines interface Fooer and struct
FooThing. Package B only requires anything that can call Foo(). But because
the interface is defined at A, now B is forever tied to A. When you change
something in A, you have to worry if it changes something in B as well.

If instead the interface Fooer is defined in B, package A can change as much
as it likes without B needing to change.

------
brightball
This is really excellent advice. It was the first thing that jumped out at me
when I read the language spec.

It's one of the reason why I think Go seems to draw more people from dynamic
languages than static because the static folks have a tendency to shoehorn
some old habits into it.

------
fiatjaf
I wished godoc.org (or similar service) somehow listed which types in a
package implemented which interfaces.

They could match with all the interfaces defined at the standard library
(io.Writer etc.) plus the interfaces defined in that same package.

~~~
chewxy
Go Guru. It's an amazing tool

~~~
fiatjaf
For code I have on my machine, right?

Well, I guess it's better than nothing.

------
holografix
Golang newbie here. Using a slice to hold different struct types of the same
interface, finding it very annoying that every time I want to access a common
attribute of the struct types I need to define a getter/setter.

~~~
flashmob
Perhaps you could try creating a struct with just the common properties, then
embed the common struct in all your 'extended' structs. Eg.
[http://www.hydrogen18.com/blog/golang-
embedding.html](http://www.hydrogen18.com/blog/golang-embedding.html)

~~~
holografix
That sounds like a bit of a hack. I like it.

------
hota_mazi
> Accept interfaces, not structs

Besides the fact that there is zero justification from this claim, the simple
fact that Go think it's important to distinguish the two is another
illustration of late 90s programming language design.

There is really no good reason to separate these concepts today, it only
causes confusion and fragments the code base in arbitrary chunks that
accumulate smelly code over the years.

But I'm happy to hear a good justification of why it's important to have both
structs and interfaces, if there is one.

~~~
yegle
Why would you think struct and interface should be treated the same? To me the
fact a struct can implement multiple interfaces means they are not on the same
"layer" of abstraction.

~~~
hota_mazi
Imagine a struct with functions that don't have an implementation: that's an
interface.

If it has some implementations and some without, it's an interface with
default implementations.

See how Kotlin does it, it's really nice and consistent, and you never need to
agonize over the decision of picking a class or a struct.

------
walesey
Great article! Makes a lot of sense.

------
ramenmeal
Would this lead to the same or very similar interfaces being defined multiple
times? Maybe that's not a problem?

~~~
cdoxsey
One of the more surprising things about Go is that you don't need to name
interfaces at all. This:

    
    
        func f1(r io.Reader) {}
    

Is the same as:

    
    
        func f2(r interface{ Read([]byte) (int, error) }) {}
    

In fact this is super common, with the empty interface:

    
    
        func f3(r interface{})
    

But somehow the ramifications of that don't sink in and we fail to generalize
the principle.

One practical usage:

    
    
        li, _ := net.Listen("tcp", ":9000")
        for {
            conn, err := li.Accept()
            if err != nil {
                if err, ok := err.(interface {
                    Temporary() bool
                }); ok && err.Temporary() {
                    time.Sleep(time.Second)
                    continue
                } else {
                    panic(err)
                }
            }
        }
    

This takes a bare interface and checks to see if it supports the "Temporary()
bool" interface (which all net errors do). When paired with a type switch this
makes for very interesting behavior:

    
    
    		switch err := err.(type) {
    		case nil:
    		case interface{ Temporary() bool }:
    			if err.Temporary() {
    				time.Sleep(time.Second)
    				continue
    			}
    			panic(err)
    		default:
    			panic(err)
    		}
    

[https://play.golang.org/p/J66vaIBGrs2](https://play.golang.org/p/J66vaIBGrs2)

All that to say, creating duplicate interfaces is perfectly fine.

~~~
chewxy
Should probably name them - makes it readable. But you are correct - duplicate
interfaces are fine

------
naikrovek
> I tend to read a lot of other peoples’ codes

"codes?"

Plural form of this type of code is "code."

Stopped reading there. This is one thing I just can't get past when I see it.

Yes, I realize that I am not a superhero-level altruist.

~~~
Scramblejams
“Codes” is idiomatic in some disciplines. For example in aerospace engineering
when discussing various analysis implementations, everyone calls them codes.
“Which vortex lattice method codes do you like?” “Oh I just use panel codes.”
And it’s not slang, you see that usage in textbooks and academic papers as
well.

------
juststeve
yeah i dont like it

