Hacker News new | past | comments | ask | show | jobs | submit login
Rob Pike on Go interfaces (groups.google.com)
104 points by fogus on Dec 20, 2011 | hide | past | favorite | 37 comments



Go OO is like Java only they took stuff away. They took away class extension and they took away the requirement that you declare interface implentations. So if you have all of the functions required for an interface then you implement the interface, no need to be more complicated.

So it's hard to see why people from an OO background (assuming an OO background like Java or c++) would have a problem with it. It's like that only simpler.


I guess go interfaces are like c++ template implicit interfaces except the interfaces have names instead of just protocols. I think the reason that it can be difficult to grasp is because it isn't as explicit. Maybe OO programmers are afraid of the crazy gcc error messages you get when you incorrectly instantiate a template in c++ and think you might have the same difficulty. Whereas explicit interface implementation declarations give you better error reporting. I have very little go experience but when I first learned about go interfaces that is where my mind went.


> they took away the requirement that you...

They removed a restriction. If you are restricted less, you can do more, but you have to learn the new things you can do. Java people can still do what they're used to: that is simpler, they just do not declare the interface implementations. But they are still not using Go fully.


I probably have a parsing problem here. Can you help me solve it?

Knowing not a lot about Go I followed the links and saw that interfaces are 'implemented' implicitly, the quacks like a duck way. If I fulfill the contract of an interface I can be used as something of that interface. Cool.

But - what is Rob's comment now?

"It's always bothered me in OO languages that we can make Circle, Square, etc. subclasses of Shape (say), but that's the one design decision we get to make. What if we want to align those things along other axes (so to speak), like topological genus or plant genus, if you're a landscaper? You might lose your way or dig yourself into a hole with multiple inheritance." (goes on, imo down the same route)

From what I can tell there's no issue here. Except for the requirement to be explicit (implements Foo, Baz, Baz) instead of implicit (just add those interfaces' methods on your type) the very same thing can archived in the biggest (market share?) OO languages. Java? Check. C#? Check.

What exactly is leading to this final quote:

"Go's interfaces aren't a variant on Java or C# interfaces, they're much more. They are a key to large-scale programming and adaptable, evolutionary design."

I'm honest here. I don't get it. Duck typing? Yeah, fine. I think I'd like that. But what am I missing? It seems in Go you get two things: No need to explicitly annotate the types with the classes it implements (good or bad, probably depends on taste and general complexity of the code) and, perhaps bigger?, you can probably extract interfaces later which are now magically implemented. But both features seem to be ~refactoring/design~ details. Nifty maybe, if they fit your style. But not a leap in any way?


Ok, so you're missing the main point. Suppose you have a library that isn't yours, and you don't have the source code. But, it has a type "Foop" that doesn't implement any of the interfaces you'd like it to for your purposes. What do you do? In the Java world, you make a subclass and implement those interfaces. We probably call that the Adapter pattern or something.

In the Go world, or the Haskell world, or even the Clojure world, we say, "No problem. I'll just extend Foop with this interface by adding these 3 methods."

Extending/adding those methods doesn't change Foop though. Foops are still Foops and can still be used as is with the library that Foop was bundled with. They just now implement a new interface for my purposes.


Just to be sure: You suggest to write interfaces ('extract' in my original post as that seems to be the refactoring name you'd use for this kind of stuff in my world) that define a contract for pre-existing characteristics of 3rd party types?

Or are you, as I read your sentence in quotes, talking about some kind of monkey patching of the original classes?

I understand your point, but I don't understand the original post/mail. Interfaces per se still seem to be the exact same thing, simple contracts, and types can fulfill multiple of those. I guess my main problem is that for me this is not a discussion about interfaces, this is a discussion about orthogonal language features which might happen to allow you to use interfaces a little more liberal.

That's why I started with my assumption of a parsing error: Reading the mail I'd summarize that as 'Go interfaces are awesome and much better than C#/Java equivalents' while the little amount of understanding I have right now lends itself far better to 'Go interfaces are - well - just like any other interfaces out there. But! We can weave them magically (java: AOP?) and implicitly in at runtime'

Still a nice feat.


"Just to be sure: You suggest to write interfaces ('extract' in my original post as that seems to be the refactoring name you'd use for this kind of stuff in my world) that define a contract for pre-existing characteristics of 3rd party types?"

No, in Go you can write new methods for pre-existing 3rd party types that you do not have the source for. It has the feel of monkey patching, but there is no classes in Go. A method is just a function that takes objects of certain type as a parameter. Knowing an object's type does not tell you what actions can be performed on it. To know that, you want to know what interfaces the object implements.

This is a one way to solve the so called expression problem, which cannot be solved by simple use of classes and interfaces in a language like Java.


In the Objective-C world, you extend it with Categories and Extensions: http://developer.apple.com/library/IOs/#documentation/Cocoa/...


One of the main advantages is that you can write interfaces which works on code in libraries you don't control, so something written without the interface in mind can 'implement' an interface added later.


You can read up the discussion on the original thread for more info: https://groups.google.com/group/golang-nuts/browse_thread/th...


I've seen a lot of criticism of the choice to use interfaces in Go, but I don't understand why. Can someone explain to me why some people seem to dislike them? From my point of view, all they are is effectively the static variant of duck-typing.


That's pretty much exactly it. Duck-typing in dynamic languages is just "I don't care what this is as long as it doesn't break when I do stuff to it," and Go's interfaces are "I don't care what this is as long as I can call these methods on it." It's basically just a way to duck-type and guarantee that the duck-typing will work at compile time.

I don't know why people dislike them, because the concept and the possibilities involved are quite impressive. (Combined with generics, they would be even more impressive...but that's another topic.)


"I don't know why people dislike them, because the concept and the possibilities involved are quite impressive. (Combined with generics, they would be even more impressive...but that's another topic.)"

Me neither, they seem great to me (same with optional typing in Dart). For me the big advantage isn't going to be so much the compile-time checks, instead its the documentation you get in the code from it (as in you can see that an argument is a shape and quickly find out what you can do with a shape).

Have to say I've done no more than play with either language though so there could be big real-world disadvantages that I'm missing...


I think it's because traditional OO models are more intuitive: People seem to innately understand that, for instance, squares and circles are both shapes; we're really, really good (too good, actually) at putting things into categories(1).

To really understand duck typing, you need to understand classical inheritance and also understand why it often sucks. Duck typing is much more abstract and harder to explain to people who haven't been bitten by deep, convoluted, incestuous inheritance trees.

(1) I think it's interesting that in Obj-c, categories are a way of injecting methods into an existing class, but if you think of the problem as a larger part of an ethereal or duck-typed system, the term 'category' makes a lot of sense: it's not an 'is-a' relationship it's a 'does-a': putting objects into buckets according to what they do (categories) seems like a more intuitive way to describe duck typing. 'you are what you eat' seems like a good starting point.

EDIT: while I'm sure the above paragraph doesn't seem mind-blowing to anyone who understands duck typing or 'categories', what i'm referring to is the terminology and the approach, I think that Obj-c may have briefly touched on a useful terminology for duck typing in general: the term 'categories', as way of thinking about duck typing, but the more general term for what I'm thinking as a 'category' is actually what Obj-c would refer to as a 'protocol'. I think if you flipped those terms, it might make more sense to people.


Do you honestly believe that people understand "This object of this subclass has all the methods of its superclass as well as all the methods of its class" better than "This object has all the methods of its declared interface type"?

Interfaces are simple: you can do X, Y, and Z to this object. Inheritance, abstract base classes, subclasses, superclasses, etc. are all much more complicated than interfaces.

You're right, humans are good at putting things into categories. So good, in fact, that practically nothing in the real world fits into one category. I'm not just a husband; I'm also a man and a programmer and a father and an American and a Californian and so on. These are all different ways to "interface" with me. To say that a single inheritance hierarchy is more intuitive than Go's interfaces is the object-oriented version of Stockholm syndrome: your vision has been so warped by so many years of C++/Java/Python OOP that you've forgotten how unintuitive it really is.

Duck typing is simple to understand. It's the natural way to understand objects. People have been doing it (kids have been doing it!) since Smalltalk in the 80s. You don't need to have "been bitten by deep, convoluted, incestuous inheritance trees" to understand or use duck typing. You don't even need to have been so traumatized to appreciate duck typing. Interfaces are simpler than inheritance and precede inheritance mentally: even most tutorial explanations of inheritance do so using the terminology of interfaces to explain how, why, and where you'll use inheritance.


IMHO you are conflating subtyping and subclassing.

The fact that something is a list carries an implicit knowledge that you can iterate it, even if the concepts of List and Iterable are just interfaces.


I know that when explaining what interfaces are, I just call them static duck-typing, and then go on from there.


> I just call them static duck-typing, and then go on from there.

Duck typing is dynamically typed by definition. This is structural typing (http://en.wikipedia.org/wiki/Structural_typing) (named, sadly, OCaml allows anonymous structural types which is even neater)


When people say "duck typing" they mean structural typing almost universally. You're not going to change them to use your terminology.

Also, names are just shorthand in Go; Interfaces can be declared and used at point of declaration, and structural typing applies to them just as well.


> When people say "duck typing" they mean structural typing almost universally. You're not going to change them to use your terminology.

No, when people say "duck typing" they're talking about dynamically typed languages which blow up at runtime or perform runtime tests on existing property. That is duck-typing, it does not take in account things like parameters or return types and it doesn't blow up at parse time.

> Also, names are just shorthand in Go;

It's not a shorthand when you have to use them (I don't know if Go can infer structural types)


> No, when people say "duck typing" they're talking about dynamically typed languages

No, they're not. Explain OCaml's object system to any Ruby or Python programmer and they'll say, "Oh, it's duck typed?" Why do you think so many programmers (such as the one you "corrected" above) use the term "duck typing" for Go? Because they mean structural subtyping. Why do you think c2.com's wiki[0] gives OCaml and C++ template as examples of static duck typing? Because they are. Duck typing (like strong/weak typing) is entirely orthogonal to whether the typing is done statically or dynamically.

You don't have to use interface names in Go. Anywhere you type a name like "io.Reader" you can type the full "interface { ... }" declaration. It sounds like you're confusing type inference with nominal typing.

[0] http://c2.com/cgi/wiki?DuckTyping


Honestly, I think I just don't understand it. When I call the method doThis() on some object implementing the interface Thingamabob, I am assured that it will do whatever Thingamabob::doThis() is obliged to do. Of course Thingamabob is short for a fully qualified name, there is no ambiguity here. And of course I am relying on whoever wrote the doThis() implementation obeying the contract of Thingamabob::doThis().

With duck-typing, how do I know that doThis() is actually an implementation of a (non-declared) interface? The chance for accidental clashes seems exceedingly high, the space of method names is not all that large. And what does it actually buy you?


The problem is you don't get compile time checking. If you forget that Foo implements IBar and change the method doSomething() so that it no longer implements IBar you only find out with runtime bugs.

I really like Go but it's been a while since I used it. Someone correct me if I'm wrong here.


  package main
   
  type IBar interface {
  	doSomething()
  }

  type Foo int

  func (f Foo) doSomething() {
  	println(f)
  }

  func test(b IBar) {
	b.doSomething()
  }

  func main() {
	f := Foo(1)
	test(f)
  }
Compile, run:

  1
Rename doSomething function to doSomethingElse. Compile:

   test.go:20: cannot use f (type Foo) as type IBar in function argument:
	Foo does not implement IBar (missing doSomething method)


Unit testing to the rescue.


More like just compiling =)


The way Rob is describing this reminds me of Haskell's Typeclasses, of which Go's interfaces are basically an ad-hoc informal version (sans default implementations) without parametric polymorphism (but that's another topic entirely).


They're similar in that they both allow a type to be associated with a supertype post-declaration, which is unusual in an OO language; they differ in that Go uses structural subtyping rather than nominal subtyping which is much more prevalent.


I had not heard the terms "structural subtyping" and "nominal subtyping" before, so thank you for that (Wikipedia was able to define them). And yes, I realize that Go's interfaces are implicit whereas Haskell's typeclasses are explicit.

I was thinking more along the lines that Haskell's typeclasses provide the exact same type of functionality that Rob is talking about wit Go's Interfaces, e.g. a type Foo can be a TopologicalGenus and a GrassGenus and a Square and an Area and a Symmetry all at the same time, by declaring the appropriate typeclass interfaces.

They're also somewhat similar from a usage standpoint, where in both Haskell and Go I can take a type Foo and declare methods on it that conform to a known type Bar (e.g. declaring an interface Bar Foo in Haskell, or simply declaring the methods from Bar in Go) and now my type Foo can be treated as that type Bar even if I don't control the source to Foo.


> Haskell's Typeclasses, of which Go's interfaces are basically an ad-hoc informal version

Uh no they're not.

Go's interfaces are just named structural supertypes.


In general I like the way Go interfaces work (we'd like to do something similar in Gosu as well); they let you decouple different parts of a system much better than you can if you have to explicitly declare interfaces.

When discussing putting something like this in Gosu, the main disadvantage we came up with was around tooling. Probably not a big issue for Go, given that I haven't really heard of too much focus on IDE/refactoring tooling there, but if one were to implement such tools, the implicit interfaces make things a bit harder. For example, if I have a Nameable interface with a getName() method, and 20 classes in my system have a getName() method but only 3 of them are actually used as Nameables, if I want to refactor the getName() method in a system like Java, I'll know which classes explicitly implement Nameable and thus which classes to refactor. In Go, it's a hard problem, because you have to know which classes are ever used as Nameables; that's no longer a property of the class itself, but rather something you have to derive from your code base. It similarly makes IDE functions like "find usages" or "find implementors" much harder to implement. (Technically they're not 100% intractable, but they're certainly much harder to do well.)

Those tooling issues are really the only drawbacks we could come up with, though. Otherwise I think the Go approach is much more flexible than the Java one.


wouldn't a bigger problem be that getName() may refer to getting a Person 's name or an enumeration of the platform or worse. ie. that you use the same names for unrelated functions which can happen and does make a certain amount of sense sometimes.


It's certainly a related problem: with Go-style implicit interfaces, neither a person nor a machine really has the context to know if something that conforms to the interface is actually intended to be used that way, or if it just happens to have the same method signatures. As you say, getName() is a pretty common sort of method name that could mean a bunch of different things, and not everything with a getName() method is necessarily interchangeable with everything else. Explicit interfaces require more work by the programmer, but make those intentions explicit to both other programmers and to automated tools.

In Gosu, we already have Java-style explicit interfaces, but we're likely going to add in Go-style implicit interfaces as a language option as well, since they're not completely interchangeable. The more I think about it, the more I think that you often want implicit interfaces for method arguments, and explicit interfaces for return values. That's oversimplifying, but as a library author you often want to say "this method works with anything that has foo() and bar() on it," so you want an implicit interface so people can use your library with existing code without changing that code to add explicit interface declarations. But if you're providing objects back as the result of a function, you might want to be more explicit and make harder guarantees about exactly what sorts of things they return.


> Moreover, you don't have to work them all out ahead of time; the whole design can evolve without invalidating early decisions.

That is a very important point IMHO. In my experience, the Achilles heel of classical C++/Java-style OO is that you tend to lock yourself into a complicated design that is difficult to modify. This goes a long way toward accounting for the recent-ish surge in popularity of dynamic languages, duck typing, etc. Being able to apply a similar design mindset in a language with static typing sounds pretty cool. (I've looked into Go a little; I think I'll look into it some more.)


Structural subtyping is not OO? Somewhere Luca Cardelli is scratching his head.


Maybe Go interfaces fit a theoretical formulation of OO, but this isn't the OO most people learned for Java or C++.


Go is a truly beautiful language; it really feels like a modern C. I hack Python and C++ for a living but would much rather hack go.




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

Search: