Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

>Go achieves inheritance through struct nesting. I find this a bit clever for my taste, and it only admits single inheritance, but it's certainly simple.

Is that really so? I thought that Go could nest multiple structs at once, and would complain if there were method name conflicts, but allow these to be resolved by defining a new method (based on the "outermost method wins" resolution rule) that could decide how to explicitly call included methods.

> Go has standard privacy through "unexported fields"

And via the "all methods must be defined with the struct" rule which prevents out of control access to internals.



Yes, you're correct, you can have more than one anonymous field. Apologies, my mistake -- I updated the post.

The resolution rules seem hairy, though; with Rust traits you can rename methods to avoid conflicts (just as in the seminal papers on traits).


Hmm, hairy how? You get forced to resolve clashes by defining a new top level method, and you can always call the included anonymous methods directly.

Seems to me, "in type T, keep included type A's implementation of X and rename B's implementation as Y" is identical in effect to "define T.X as calling self.A.X() and define T.Y as calling self.B.X()"


I suppose they aren't that hairy if you introduce type-specific namespacing like C++ does; i.e. allowing method names to be qualified with the name of the class or trait they belong to to resolve conflicts (A::X and B::X). Renaming seems like a way to avoid having to create an extra delegation function in case you want to delegate to one or the other, though.

It's important to keep in mind that traits aren't the same as inheritance, though. Traits combine methods and fields into one final class instance (they're like mixins), while anonymous fields are more like C++ inheritance: there's actually an instance of each superclass embedded inside each subclass. This starts to matter especially when you have diamond patterns (A derives B and C, B and C derive D). When you have an instance of D and are calling methods of A on it, does each method of B's embedded A clash with that of C's embedded A in Go?


Go's anonymous fields are always accessible as a struct member named after their type, basically. It isn't namespacing, it's syntactic sugar that kinda-sorta removes their anonymity. Diamond inheritance in Go means you have physical nesting like [D [C [A]] [B [A]]] so necessarily the A's will clash, but you can override their methods and manually resolve it - yeah, at the cost of a delegating call (which the compiler could probably strip out).

Interfaces subsume the feature of traits to define an operation abstractly against the methods of its operand without defining its representation. Unlike traits though they can't mess with member variables directly (of course methods/interfaces can be defined on things that lack members, such as a renamed int32).


Traits are actually about code reuse, not about polymorphism. Indeed, in the original traits literature (and in Rust), traits are not types at all. You use interfaces in Rust for that, just as you do in Go (they work very similarly).

Think of traits as little pieces you can glue together to make a class.


Pure-code mixins and methods on Go interfaces are sort of duals of one another. Interface methods are parameterized with a type when called ("calling outwards"), mixins are parameterized by a type when compiled, and merged into it ("calling inwards").




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

Search: