Coming from other languages, the most interesting thing about Go for me (in my limited experience of a few other languages) was all those things they left out:
No inheritance - no more digging through the massive world-tree of objects to find the code that actually does things.
No churn - I have not seen a Go update break my code in about 8 years of use.
No complexity - I like the culture of simplicity and eschewing dependencies in favour of writing the minimum code required.
No dynamic libraries - deployment is easier and apps more stable
No declared interfaces - they are defined at the point of use, not declared elsewhere
No header files - why C++, why?
No implicit type conversion - of the kind that plagues JS (see WAT), this rarely makes it more verbose.
There are of course a few gnarly corners - nils, errors, panics, struct tags are not very satisfactory IMO at the moment.
There are also lots of great positive things about it – the GC, tooling, fast compiler, stdlib and docs are a great example for other languages IMO.
I'll be really interested to see where they take it next, while hopefully keeping the culture that has made it so pleasant to use. Thanks to everyone working on Go from me, and here's to another 12 years of Go.
"No inheritance - no more digging through the massive world-tree of objects to find the code that actually does things."
You still have interface methods? I had problems navigating a new codebase and find the places "actually doing things". Some object was passed in somewhere which mysteriously implemented a one method interface defined on the spot in the other go file. I.e. the implementation had no relation to the interface which was obvious without a lot of searching and finding the right implementation.
Also passing in functions (callbacks?) all over the place is a little messy at least for a newbie. It was hard to find where the functions where called and at what point and how the program flowed.
(this is hard to explain but maybe somebody gets the point...)
edit: somebody else touched on what I also meant: " duck typing make refactoring and understanding new codebases error prone"
Also the modules and dependencies management surely is a joke? Pulling stuff from github willy-nilly? Quite bad in any case when compared to maven where you have a local repository with all the dependencies (so they don't change or disappear from the internet so that you can actually build your software 5 years later, exactly in the same way)
There are certainly ways to write confusing code in Go too, no inheritance is just one cause of confusion subtracted.
IMO interfaces are best used sparingly and in a minimalist way like io.Reader (but I prefer them to Java interfaces and have not found them confusing nor felt compelled to find all concrete types conforming), callbacks are best avoided if possible, and yes dependency management has only recently improved.
I really do think Go has a discovery problem. Function signatures don’t tell you hardly anything, does this need something deferred, what types does it implement, etc.
> No inheritance - no more digging through the massive world-tree of objects to find the code that actually does things.
That's not 100% accurate; as a concrete example, tell me which files (to say nothing of the actual downstream types!) contain the implementations of this interface method: https://github.com/kubecost/cost-model/blob/v1.88.0/pkg/clou...(err, without using github's fancy new SourceGraph-lite integration, of course, that'd be cheating)
I find the sibling "No declared interfaces - they are defined at the point of use, not declared elsewhere" similarly suspicious, but suspect we're having a nomenclature mismatch
> That's not 100% accurate; as a concrete example, contain the implementations of this interface method
But why? I've often wanted to know what a method does in Ruby and have had to resort to .method(:x).source_location because it is so dynamic only the compiler knows once it has finished running it. I've never had to find all the places that conform to an interface in Go (a very different question) or Java, because that's what an interface is for, so that you don't need to know, and new people can come along and conform too: your interest should be limited to what they can do, which the interface tells you already. Maybe this is a problem in getting to know a large complex codebase I guess? I've never encountered it in the real world.
The second point is linked - interfaces are used the way you describe in a few other languages, Java among them, (find me all the implementors of x), but not in Go, the whole point is you have no idea who the implementors of an interface are, new ones will arrive, and that's ok.
It's certainly possible to write bad, confusing and enterprise Java flavoured code in Go, but the lack of inheritance at least does away with a whole bag of hurts related to overabstraction.
> your interest should be limited to what they can do, which the interface tells you already
That's a very idealistic perspective, and my sincere congratulations that every codebase you've worked with so far has been so great as to completely and unambiguously document every edge case and pre/post condition.
If we stick just to the cited example:
Features() string // Features are a comma separated string of node metadata that could match pricing
great ... so I'm guessing if there are no such features it returns "" then, but otherwise it ... just contains the metadata keys? key=value as CSV? It escapes any "," found in the values with \\, does it? What's an example? Well, normally I'd go look at the implementations but in this awesome world of fully specified godoc I guess I shouldn't worry myself with such details
I'm almost sorry I replied to this, because we are clearly living in such different universes, but I am actually genuinely interested in reading the URL of the godoc you've experienced that is so perfect that one need not ever bother with how many disparate implementations there are
I sincerely have never had a problem with interfaces and never wanted to answer this question, but I suspect I use them far less than a lot of people who seem to use interfaces for almost every function argument in order to use them for testing or out of habit (or use code that does).
This interface is not great as it asks for ambiguous info and three methods.
I'd look at the docs, and what the callee does with .Features() for guidance (which in well written code will usually be in the same package as the interface, ideally the same file), not what one implementation happens to do - otherwise what's the point of the interface as you're coding to one implementation?
So I guess my answer is a variant on 'No true Go programmer would use interfaces this way', genuinely sorry about that, as you say we probably live in different worlds - I don't work on kubernetes or things inspired by it. If this is a big problem for you though, I reckon tooling could solve it for you, as you pointed out.
Most interfaces in Go code have exactly two implementors that will ever be plugged in: the production code and the mock. You want to be able to delve down through the layers of production code, e.g. handler to controller to gateway, to trace what's going on in an RPC request.
They do have a few little DSLs, which I dislike: struct tags (optional, I prefer to avoid), magic comments which provide build directives (this seems icky to me, but avoids breaking Go1 promises I guess).
I don't want to be too contrarian, but I'm not aware of any library which uses struct tags to encode anything that anyone might call a DSL. At most, they're used for key-value pairs (e.g., `foo:bar`), which is pretty easy to get one's head around. I don't use build directives and ideally we wouldn't need them, but most (all?) mainstream compiled languages have them. Maybe the complaint is that they don't get their own dedicated syntax, in which case I don't see the issue beyond "in $MY_PREFERED_LANG, build directives have their own syntax, and that's how I like it!".
In all cases, these issues seem positively trivial compared to "you can't parse JSON or send an HTTP request until you learn some DSL or cargo-cult someone else's build file".
"I'm not aware of any library which uses struct tags to encode anything that anyone might call a DSL."
There's a couple of struct validation libraries that got IMHO a bit overexcited with what you can jam in a struct tag and then interpret, but they don't seem to have gotten very popular, so it doesn't factor into the language much.
Have a look at some of the bugs related to struct tags (420 open ones) - they can control marshalling and have lots of little directives in them like omitempty,attr,-,set plus they combine too (for xml,json,asn1 etc) and you can stuff your own little language in too if you want, the possibilities are endless! They are a set of limited yet unvalidated translation DSLs stuffed into a string.
Personally I think the language would be better without them, but it's too late now.
Re build directives - the complaint is they are comments which do things and change code/compilation, the syntax I don't care about, but I do care that they've abused comments to do this.
I agree, these problems are pretty trivial, I don't lose sleep over them.
No inheritance - no more digging through the massive world-tree of objects to find the code that actually does things.
No churn - I have not seen a Go update break my code in about 8 years of use.
No complexity - I like the culture of simplicity and eschewing dependencies in favour of writing the minimum code required.
No dynamic libraries - deployment is easier and apps more stable
No declared interfaces - they are defined at the point of use, not declared elsewhere
No header files - why C++, why?
No implicit type conversion - of the kind that plagues JS (see WAT), this rarely makes it more verbose.
There are of course a few gnarly corners - nils, errors, panics, struct tags are not very satisfactory IMO at the moment.
There are also lots of great positive things about it – the GC, tooling, fast compiler, stdlib and docs are a great example for other languages IMO.
I'll be really interested to see where they take it next, while hopefully keeping the culture that has made it so pleasant to use. Thanks to everyone working on Go from me, and here's to another 12 years of Go.