Golang Object Oriented Design
nathany.com
nathany.com
Yet Go keeps that old OO concept of method receivers. Why? I never found it very plausible to have one special case parameter with its very own syntax, but with type level encapsulation it did at least have some justification.
Without this kind of encapsulation, the only remaining purpose of the receiver is method dispatch. But why dispatch based on the type of one parameter and not the others? Multimethods would have been the logical conclusion of the OO cleanup that Go's designers apparently intended to achieve.
How unpleasant that'd be! With method receivers, you can have `addr1.CompareTo(addr2)` and `addr.ToString()` etc..
Methods are just a limited special case of functions. Also, there are various different semantics that are used in function application. Your view of function application is rather limited, you might want to study e.g. how functions are applied in Haskell (type classes) or Common Lisp Object System (CLOS, multimethods). Both of them use very different semantics to what you're used to.
In addition, good namespacing and module systems can solve a big part of the "problem" you mention. Most modern languages have more than one giant global namespace.
Well, sure. On the other hand, if you had receivers for record accessors, you wouldn't end up with the ugly prefixing necessary in Haskell.
They would allow `ToString(attr)` to have one implementation per type, and `CompareTo(item1, item2)` to have one implementation per `(typeof item1, typeof item2)` pair. The latter being unavailable in Go, and only statically available in languages with overloading.
/Edit: parallel comments mention multimethods. The difference is that with higher order type inference you can do static dispatch, but multimethods are dynamically dispatched AFAIK.
This leads to very hairy inference problems, which is why none of the ML family languages (that I know of) allow ad-hoc overloading of functions.
type IPConn struct {
ip Ip
// contains filtered or unexported fields
}
func Write(i IPCconn, buf []byte) (int, error) { /* implementation */ }
type File struct {
name string
// contains filtered or unexported fields
}
func Write(f File, buf []byte) (int, error) { /* implementation */ }
func WriteString(w T, s string) (int, error) {
return Write(w, []byte(s))
}
Please note that there are no explicit interfaces, but WriteString takes an implicit Writer. You can do that in C++ with overloaded functions and templates (not overloaded functions alone), but templates are an example of higher order type inference, which was the point I was trying to convey.Your global namespace would be so incredibly polluted.
Typeclasses have a lot of tricky corner cases in defining their semantics, they're hard to type-inference (Haskell's type system is undecidable in some cases), and they lead to a lot of confusion for new programmers trying to learn the language. MPTCs were deliberately not standardized in Haskell 2010, because the design space around them hasn't been fully explored, and SPJ's feeling was that Associated Types might be a better solution for the problems involved.
Go's designers have decided to focus on pragmatism over power (wisely, IMHO - I quibble with a lot of the other aspects of Go's language design, but not this one), and the result is that they get about 85% of the power of multimethods/typeclasses with about 10% of the headaches.
Typeclasses are completely different from Go interfaces in a way that's totally unrelated to single versus multiple dispatch: in Haskell they're a bound on generic functions that specifies permissible operations, while Go interfaces are existential wrapped values. To give a concrete example of the difference here, you can write a function that takes a List of Eq values and pass a list of integers to it in Haskell, but in Go you cannot pass []int to a function that takes []Equal without recreating the list. On the other hand, in Go you can have a heterogeneous array of Equal interfaces, while in Haskell this is not possible without existential types.
> they're hard to type-inference (Haskell's type system is undecidable in some cases)
This is only when you combine the feature with type inference for functions (which Go doesn't have) and H-M style inference (which Go also doesn't have). Typeclasses would pose no problems for Go's extremely limited inference.
Pitting typeclasses against interfaces makes little sense to me; typeclasses are an enhancement to generics, while interfaces are about runtime polymorphism. Typeclasses have no meaning in a language without generics like Go (although I personally think Go will need generics eventually).
Te first argument is usually a variable already declared one more more lines above your cursor, so you can start with that. Then your IDE kicks in and gives you a list of possible methods. In this case it is easy, if you want to use "length" or "size" for your list.
The "dot-autocompletion" has a pretty good usability in my opinion. You write down the name of the variable, enter a dot and you get a filtered list of methods that "make sense" to call on the receiver (key word: discoverability of the API).
How exactly would one implement the trigger with multimethods? I think this is an important consideration to make from a usability standpoint.
I admit it's a little weird, but not so weird people couldn't get used to it.
x y x DoSomething
However, I don't think I've seen an IDE for PostScript or ForthMethods are useful for implementing interfaces and for avoiding namespace clutter; otherwise they're equivalent to functions.
I have no idea how you get from multimethods to interfaces.
You want to say Go doesn't have an explicit "this" pointer.
http://www.amazon.de/Component-Software-Beyond-Object-Orient...
The first edition used Component Pascal from the Oberon family, which Go gets some influence from. Later editions used Java and C# instead.
Additional learning about how COM and XPCOM work is also a way to get some ideas.
"Component Oriented Programming = Polymorphism + (Really) Late Binding + (Real, Enforced) Encapsulation + Interface Inheritance + Binary Reuse"
Apart from "Binary Reuse", the definition above is what object oriented programming is really all about (I think).
If you have to apply it, it isn't uniform access. The point of the principle is that the call-site shouldn't distinguish between getters and fields so that the implementer can change that decision freely without breaking callers.
Nice article, though!
Do I have to implement my own []Foo type with all three methods as described in the sort package?
That is one of the reasons go has a guiding design principle that interfaces should be as small as possible.
Self, JavaScript, CLOS, Go, BETA, Clojure Protocols, Type Classes, COM and probably quite a few others.
What most mainstream developers know, is not the only way.
Never saw it outside the conference.
For example, `inner` being the opposite of super (e.g. call the subclasses implementation of this method). Some others, but I'm not sure I remember them (Augmentation?).
The question is not where the type syntactically is. The question is, if the dispatching between functions/methods happens dynamically or statically.
For example, say you're writing a CMS and want to make strings like the article title to be used in the URL (for readable URLs and SEO), displayed in HTML (eg inside heading tags) and also potentially used for string comparisons, you'd need to have a function to URL / HTML encode the string and you'd need to remember to do it each time you outputted the string. This can make it very easy to introduce vulnerabilities where you forget to HTML encode the string. With methods, you can force the developer to state which format to output the string as:
type DisplayText struct {
Value string
}
func (dt DisplayText) HTMLEscaped() string {
return html.EscapeString(dt.Value)
}
func (dt DisplayText) URLEscaped() string {
return url.QueryEscape(dt.Value)
}
var article_title DisplayText
So now when ever you call article_title, you have to specify the string encoding. Which is not only more secure (eliminates the risk of forgetting to encode your string), but also more readable: fmt.Println(article_title.HTMLEscaped)
vs fmt.Println(html.EscapeString(article_title)) fmt.Println(html.EscapeString(article_title))Like Self or javascript?
Yes. Really, interfaces are all you need for OO; class are just types-that-also-define-interfaces, and inheritance is just a shortcut for interface implementation.
While I think Go's implicit interface implementation is pretty much 180-degrees off the best way to do OO w/o classes and inheritance (I'd prefer explicit interface implementation, which eliminates interface collision), I do think clasess and inheritance get in the way more than they help.
This is a clear example of that. Implementing “polymorphism” with Go interfaces is abusing interfaces! Seeing embedding as inheritance misses the point of both embedding and inheritance. Comparing packages to namespaces isn’t that bad, but just delays you getting to know Go for real.
I’m not saying that these old concepts are bad or Go’s new approach is superior. (I believe in that; but that’s not the point here.) I’m saying if you’ve gotten used to these concepts so much that you can’t think or program without them, then there’s a problem. Don’t design a solution around polymorphism or inheritance and then try hard to force that into Go. Design your program with that your language is giving you.
> Composition, embedding and interfaces provide powerful tools for Object-Oriented Design in Go. Thinking in terms of inheritance really doesn't work. Trust me, I tried.
The OP is clearly trying to introduce ideas in Go by relating them to other ideas you might be more familiar with. This is, IMO, very much distinct from trying to shoehorn design techniques from other languages.
In fact, the OP is rather explicitly agreeing with your point and trying to remedy it.
> Implementing “polymorphism” with Go interfaces is abusing interfaces!
Go's interfaces use structural subtyping, which is a form of polymorphism.
I've never seen a book with 24-point body type, except possibly for the visually impaired or 3-year-olds. Heck, most headings aren't even 24 point. What is it with this blog trend of gargantuan type, which seems increasingly common? It's totally out of proportion with the rest of anyone's OS and computer interface, and all the common sites. I'm really getting tired of having to zoom out to an insane 50% level just to make things legible again.
P.S. the font-size is actually 1.5rem
Do you not wear your glasses and/or contacts when working? I don't follow.
The only critique I have is that there's no way to verify that object X implements methods A, B, and C. When I'm using C# or Java, I often use an interface as a test to make sure I've done my work since the code won't compile if a class that extends an interface fails to implement all the methods.
var _ Interface = Concrete{}