Hacker News new | past | comments | ask | show | jobs | submit login
The ecosystem of the Go programming language (henvic.dev)
204 points by henvic on March 22, 2021 | hide | past | favorite | 251 comments



Go is not a simple language. Please learn from my mistakes, and ignore arbitrary metrics like how go has fewer keywords. The complexity doesn't go away, it's hidden away somewhere.

If anybody wants to learn go, ignore everything about how easy it is to learn. Because it's extremely deceptive. Take time to actually read through a lot of the important underlying concepts like how slices work.

Ignore comments about how go is simple to write. There's a fair amount of bad go code and people break rules all the time. From official sdks to standard libs. Go can be extremely difficult to both write and read. It's implicit interfaces and judicious abuse of interface{} can make tracing how a library is supposed to work a PITA. Writing go can be difficult because of a lack of some language constructs that leave you unsure how to proceed.

If you can, use goland. I use vscode and it's... not pretty. Almost every single thread I've seen on reddit recommends goland. My experience with vscode has been very unpleasant, particularly with go plus.

OTOH, things like criticisms about it's error handling are overblown. At best it's a mild nuisance, and more often than not, a good thing that makes error handling explicit and clear.

There are many things about go that have made me appreciate it's existence. There are also many things that have left me absolutely baffled at how bad it is.


> Ignore comments about how go is simple to write. There's a fair amount of bad go code and people break rules all the time.

This is one of the most famous straw man arguments made in software development and I've seen it used against a plethora of different programming languages -- each time completely missing the point that you can get bad developers writing bad code in any language. The fact it happens in Go isn't a testament not to how difficult it is but rather how good of a job the language has done attracting newer developers.

> Go is not a simple language. Please learn from my mistakes, and ignore arbitrary metrics like how go has fewer keywords. The complexity doesn't go away, it's hidden away somewhere.

I completely disagree. Having fewer keywords does make Go simpler to learn and simpler to read other peoples code. The opinionated nature of Go also makes it simpler to collaborate.

Writing software isn't simple but that complexity of turning ideas into code doesn't change significantly from one language to another (generally speaking). You still have to solve a problem using logic and functions. I find the harder problems with software development is collaboration (eg ensuring everyone is following the same coding styles) and reading back old code. Go makes those things easier than some -- not all but definitely some -- languages.

I say this based on 30 years of experience writing software in more than a dozen different programming languages.


> Having fewer keywords does make Go simpler to learn and simpler to read other peoples code

Go carefully touts how few reserved keywords it has, but I think it would actually be a simpler to reason about language if it had more reserved keywords.

For example, you may notice that "true", "false", "len", "new", etc all aren't reserved keywords.

This sometimes does lead to bugs, for example someone may make a function, like "func memoize(new func(...interface{}) interface{}) func(...interface{}) interface{}", which shadows a common builtin function with a function argument.

Usually, the above leads to a compiler error when you try to use the "copy" argument (a value) as a function, but every once in a while, you end up with a type-compatible shadow, at which point things can really break.

It would be less confusing if things like "new", "len", "copy", and "true" were keywords, rather than builtins that someone may accidentally alias and shadow.

If you account for things like rather essential builtins as well, go's list of keywords starts to look far less impressive, and on-par with java or C#.


Speaking of reserved words, I remember reading once about how PL/I had no reserved words. Which has the nice quirk that this is valid code:

    IF IF = THEN THEN THEN = ELSE ELSE ELSE = ENDIF ENDIF
...assuming IF, THEN, ELSE, and ENDIF are all variables.


true := false is valid golang code



Wow, WTF? Why are true/false not reserved?


They were worried about internationalization and they though reserving vrai/faux, adevărat/fals, etc would be overkill.

I'm kidding. It does seem like a strange decision...


> Having fewer keywords does make Go simpler to learn and simpler to read other peoples code.

that sounds like saying that latin-alphabet languages are easier to read than chinese or japanese because they have fewer characters. the complexity just goes in the way those characters are assembled together.


Funny you should use that as an example because one of my lockdown projects has been to learn to read simplified Chinese and I'd say your point about Latin-alphabet languages was also true despite it being intended as a falsehood. Learning all the different Hanzi is immensely harder because of the number of characters and how intricate they often are. I'd also say that last point means it's also easier to write Latin characters too.


> Funny you should use that as an example because one of my lockdown projects has been to learn to read simplified Chinese and I'd say your point about Latin-alphabet languages was also true despite it being intended as a falsehood.

but that's because you come from a latin background. I lived with a japanese girl for a long time and she was adamant that kanji were much easier and faster to parse for her than western text


I can see some kind of argument that logograms are easier to parse than an alphabet because you can communicate more information in a smaller space, but that's only true if you already committed the thousands (!) of logograms to memory in the first place. This is no trivial task!

The question is if it's worth putting a 5+ year barrier in front of people in order to become fluent in a written language that maybe, possibly, might be "faster" once you are fluent. For native speakers, perhaps that's worth the effort (although there are plenty who argue otherwise). For second-language speakers, I think the answer is almost certainly no.

So bringing it back to Go. Perhaps Go seems more difficult and slow to people who already invested the years required to become fluent with something like C++. I doubt that, but I guess it's possible. But Go isn't designed for that case. It's designed as a second language that any programmer can learn quickly and become reasonably productive in.

If you have a company with 1000 programmers who all know different languages, and you want to start creating good software right now, would you standardize on the language that all 1000 programmers can learn in a couple of weeks, or the language that 100 programmers know inside out and the other 900 are going to spend a year figuring out?


I took a year of Mandarin as an undergrad, and learned ~1,500 characters (at my best). The two approaches are just radically different (pun intended), with an alphabetic writing system (not just Latin, but Arabic, Cyrillic, etc.) if you know the system, you can read texts in other languages that share that system aloud - without knowing what you're saying. With logographic systems like Hanzi, you may be able to guess at the meaning of a character you don't know by looking at its radical, but you have no idea how to pronounce it. I do think broadly speaking alphabets are easier to teach and to learn, though still not easy (ask any small child).


> but that's because you come from a latin background. I lived with a japanese girl for a long time and she was adamant that kanji were much easier and faster to parse for her than western text

You could make your same argument here and say "that's because she comes from a Japanese background" ;)


but that is precisely my original argument. in the end everyone finds "simple" what they're accustomed to, and you end up with more-or-less the same amount of complexity overall, no matter the system used to express logical thoughts, if you let humans try to optimize it over decades (but systematically, for programming languages) or generations (but less systematically, for spoken languages).

I'm mostly programming in C++ and I find understanding what happens in some Python scripts much harder than some template metaprograms I've worked on for instance.


You're conflating different things now: simplicity vs familiarity.

If someone is already a C++ guru then writing something in C++ for them will be easier than learning Go. But that doesn't mean that Go is easier to learn than C++.

When talking about simplicity people generally compare all languages as if one has little or no familiarity with them. Otherwise it's not a fair comparison.


> You're conflating different things now: simplicity vs familiarity.

we're talking about humans, you cannot not conflate them.

> When talking about simplicity people generally compare all languages as if one has little or no familiarity with them.

that's a philosophically pointless exercice. Things have to be assessed in their actual, real-world context to bear any relevance !


...and you asses something by comparison. Having different starting points isn't a comparison, it's a bias. I don't have an issue with people having a bias (we all have our own preferences, our own strengths and weaknesses, and stuff we just enjoy more than other stuff). But lets not pretend it's anything more than a bias if you're not willing to compare things from an even starting point.


All you need to do is look at how many, many more hours Japanese students have to put into studying kanji to know that your perspective isn't accurate. The basics of learning Japanese are much more difficult that a latin language. What is more, the complexity means that people are very prone to forget how to even write kanji. To the degree that is is often a game show topic.


Part of the hard part in learning Chinese and Japanese is that there is a separate phoenetic "alphabet" you have to learn to properly pronounce characters. (at least in Taiwan, kids learn bopofomo)


Native Japanese never learn alphabets for pronounce.


Can I derail this to ask how you've been going about your project? I had the same lockdown project but with Japanese. It's been going well. I've found a little company called Wanikani that does a great implementation of a spaced repetition system. I've never rigorously used one before, but based on my progress, I'm now a believer in the technique and am a bit mad at myself for not doing this for school subjects as a kid.


The argument may be wrong, but the conclusion is true: literacy rates are actually lower in countries with ideographic languages — likely because there are more properties of words in ideographic languages that need to be memorized "literally", and fewer properties that can be intuited, or easily "sketched" verbally in order to ask the real spelling/pronunciation/etc.

One study I recall (on my phone now, will cite later) reported that fewer 20-year-olds have "fluent" levels of reading comprehension for what they read in the [ideographic] newspaper in Japan or China, than in neighbouring countries with alphabetic languages derived from those same historical ideographic roots (Korea, Vietnam, Thailand.)

Another study (will cite later) reported that the reading-comprehension rate for ideograph-heavy works in Japan is also decreasing over time, as more people spend their time reading mostly explicit "[ideograph-]light" novels, rather than literature that would actually teach them kanji. (Which is a vicious cycle.)

Given that ideograph radicals are kind of analogous to alphabetic morphemes, this is kind of surprising. I think it's mostly because it's self-evident how to pronounce alphabetic morphemes if you know the pronunciation rules of the language, while

1. the names for ideographic radicals need to be memorized, more like the names of alphabetic letters (but with there usually being far more radicals in common use per ideographic language, than there are letters in an average alphabet.) And

2. ideograms aren't usually linear, but rather structured (things above/below/beside things, things inside things, things bonded to other things and reduced in the process, etc); and there's no universal cultural fluency for speakers of these languages in an ordering system for spelling out these radicals. You usually have to see a character written before you can truly know how to "spell out" its radicals.

Because of these two problems, you can't just look at an ideograph you don't know, and casually, conversationally spell it out to someone else, in order to ask how it's pronounced / whether you're pronouncing it correctly. Unless, that is, you both know the names of all the involved radicals, and have a good geometric guess for the ordering. In practice, most people don't try to ask this question verbally, but either need to show someone the character written; or need to use a dictionary.

I've noticed that this actually results in distinct approaches to how people treat "literature with words they don't know", between people whose first language is an alphabetic one vs. people whose first language is an alphabetic one. Given an alphabetic-language work with word they don't know, alphabetic-native people seem to be much more confident in just jumping in and looking words up as they go; while ideographic-native people seem to be more reticent. I believe they're subconsciously expecting new [alphabetic] words to be a much larger barrier for them than they actually would be, as they're used to thinking in terms of the barrier that novel ideographs impose.


> literacy rates are actually lower in countries with ideographic languages

The World Bank puts China (97%) behind Korea (98%), but ahead of Vietnam (95%) and Thailand (94%). They don't have data on Japan, but I'd be surprised if their literacy rate were much lower. (I'm also on my phone, but here's the link: https://data.worldbank.org/indicator/SE.ADT.LITR.ZS?location... )

I think those literacy rates mostly reflect the quality of these countries' respective education systems, rather than any inherent properties of the scripts used.


IDK why do they downvote you; the issues with logographic languages are evident. With Spanish for example, you have a very regular spelling plus far fewer symbols. You can guess the spelling of the word by just reading it, and the opposite it's true. You may transpose a b/v, or omit an h, but that's it.


It would be interesting to find someone that grew up knowing a third type of language (no idea what that is, though!) and that then learned latin alphabet and chinese (say) as an adult and hear what they say :)


Arabic, Hebrew, or one of the Indian subcontinent languages.


Those all use alphabets or syllabaries, so it's just a matter of learning a small new set of symbols.


Yes and no. There are complex rules around joining letters depending on where they fall in the words. Also there are no vowels but sometimes there are vowel markers.


I meant that if you know Hebrew, Arabic, or Hindu, learning the Latin alphabet is a lot easier than learning Chinese characters. And Latin user will probably find Arabic easier than Chinese too, even though the Latin alphabet is a bit simpler than Arabic.


It also depends a lot on the language. There are dozens of languages that use the same core Latin characters plus a few decorators. Someone coming from a strongly inflected, gendered language trying to learn English is going to struggle compared to trying to learn Spanish or French.


I guess it depends on which Latin alphabet variation. Vietnamese or Polish speakers will probably not feel intimidated by Arabic :-p


> This is one of the most famous straw man arguments made in software development and I've seen it used against a plethora of different programming languages -- each time completely missing the point that you can get bad developers writing bad code in any language.

This is a fair point since I wasn't very clear, and I am talking about something that's fairly subjective and abstract. I think the more claims a language makes, the more open it is to failure. I just googled is "rust difficult to learn", and the first answer is "no". It's all relative, but I am sure that most of us have heard that rust does have a steep learning curve. The only other language that I can think of that advertises itself as easy is python. And I think it has a lot of faults in those claims too. But python does a much better job of being easy than go does. It's completely different leagues.

Again, this is a messy point to make, either somebody understand what I am trying to say or they should leave it at that because trying to qualify ideas like this is fuitless.

> I completely disagree. Having fewer keywords does make Go simpler to learn and simpler to read other peoples code.

I really don't think skipping keywords like public and using casing makes things simpler. A quick google search shows that go has 6 less keywords than c. I mean, c'mon...

> I say this based on 30 years of experience writing software in more than a dozen different programming languages.

If you've had years of experience in old school c/cpp, then (I would assume) go will be a lot easier, and a lot better in comparison.


> If you've had years of experience in old school c/cpp, then (I would assume) go will be a lot easier, and a lot better in comparison.

In my opinion old school C++ was a lot easier to learn than modern C++. I'm talking about the language specification specifically. The tooling is undoubtedly better.

Visual Basic 6 easily wins the "easiest language" award in my opinion though. But it's often seen as a faux pas to mention VB amongst developers


But some languages make it easier to write bad code than others.


> Having fewer keywords does make Go simpler to learn and simpler to read other peoples code

I partly disagree. It might be easier for you with 30 years of experience. Sysadmin with 8yrs of experience and having to manage operations utilizing Go is not easy.

The code looks neat on the page; under the hood is really un-understandable at times unlike Perl, Ruby, TCL where the code is staring you in the face. The importation of modules off github is fundamentally wrong too.


> The code looks neat on the page under the hood is really non-understandable at times. Unlike Perl or TCL where the code is staring you in the face.

Are you able to elaborate on this a little more? I love Perl and in fact that was my favourite language for a long time (despite all of its warts). But languages like Go are a lot more explicit that Perl so I'm not really sure what you mean by "under the hood" and "code is staring you in the face.

> On vent: I also really loathe importing modules off github.

I must admit that really annoyed me too but in fairness it's a pretty common pattern (eg brew, npm, etc). In a way Perl (CPAN) is an edge case rather than the norm. The difference is other systems add a shim around the code source even if they do still pull directly from (or from a cache populated directly from) Github et al


Sure.

We use Prometheus exporters, most of the exporters are written in Go. And this is a snip from the vmware-exporter for our VMware estate.

    metrics.go
The first throw was this:

    import (
 "context"

   "github.com/prometheus/client_golang/prometheus"
 log "github.com/sirupsen/logrus"
 "github.com/vmware/govmomi/find"
 "github.com/vmware/govmomi/view"
 "github.com/vmware/govmomi/vim25/mo"
 "github.com/vmware/govmomi/vim25/types"
So as it currently stands I have no idea what those modules are and what I am loading. Browsing each of those repos sure will tell me, but that's not my role. My role is to make X operational.

And if that repo doesn't exist anymore and you go to compile, it fails. More overhead if I need to make changes to any of those modules. I would need to git clone it somewhere, make the change and then either recommit it to git, or do something to make it locally available. At least with NPM, CPAN they are local to the file system.

So lets start:

     var (
 prometheusHostPowerState = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  Namespace: namespace,
  Subsystem: "host",
  Name:      "power_state",
  Help:      "poweredOn 1, poweredOff 2, standBy 3, other 0",
 }, []string{"host_name"})
I understand all of that. Power_state, display the help, power status description etc, however prometheus.NewGaugeVec -- Sounds great. What is it? Where do I find this? What module is this related too?

That's where it falls down for me. I now have to duck in to the rabbit hole of github to find out where that function is phrased and in this sense "under-the-hood" could also count for library .pm files in perl, but now having to navigate GitHub really messes with my workflow.


> So as it currently stands I have no idea what those modules are and what am I loading? vim25? Is that the text editor vim?

Take any of these imports, append it to https://godoc.org/, and see.

For example, github.com/vmware/govmomi/ becomes http://godoc.org/github.com/vmware/govmomi/ , which tells you:

    govmovi: A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter).
Everything underneath govmovi is subpackages of it, so `find`, `view`, `vim25/mo`, and `vim25/types` are all govmovi packages. You can also visit them on godoc to read documentation, if the code authors followed documentation best practices.

These are imports like in any other language. They are, however, nearly always based on a global URL, and thus have a godoc page that you can visit and read the docs for immediately. They are also always qualified, so if you wanna find all all uses of govmovi/vim25/mo, just C-f for "mo." in your codebase. No `from foo import *` equivalent exists in Go, which is one of my favourite features of this language.


Sure. Fair to say that godocs is to cpan of perl.

But Go doesn't present you the code on disk. I have to hunt somewhere else with a reference. With a library file, I know that prometheus::exporter::blah is related to the perl module prometherus, exporter subroutine blah

I need the code in front of me to understand how it's interacting; the whole github ruins that for me.


If you want to read the code, each godoc name can be clicked to directly redirect you to the source code of what you're looking for, eg. to a github page code viewer.

Depending on whether you use gomodules, GOPATH, or any other build system you also can find the files locally to grep through them or use your text editor to read them locally. You can also override import resolution to point to a fork (eg. of any module if using gomodules), if you want to test drive some local patches.

I do a _ton_ of code reading in my work, and I find using Godoc for that probably the best experience of any language (second only to maybe Haskell/Hoogle, but there you get much more difficult-to-grok-at-a-glance code).


> You can also override import resolution to point to a fork (eg. of any module if using gomodules), if you want to test drive some local patches.

So much overhead where time is limited. I shouldn't need to fork packages, I don't want to fork.


It's one `cp -r` and a 1 line change in go.mod [1]. You can also just `go mod vendor` to have all modules land in a `vendor/` directory when you can edit things as you wish [2].

[1] - https://golang.org/ref/mod#go-mod-file-replace

[2] - https://golang.org/ref/mod#vendoring


> But Go doesn't present you the code on disk.

Yes it does. You couldn't compile the modules if they weren't on disk :)


But only at compile time.


`go mod download`, if you're using go modules, will fetch you the deps.

This is no different, to me, than downloading a tarball of Python software with requirements.txt and not running setup.py or pip install. I don't understand the problem.


Right, then it could be I wasn't aware there is a "go mod download". If it actually gives me a file I can modify without having to create any repo, special things to apply the changes then that will assist me in the future.

Go isn't my thing and the less time I play with it, the happier I am. This is only my experience of it, and my experience is it acts friendly, but is a faff.


> the less time I play with it, the happier I am.

There lies the problem. If you don't want to invest the time to learn the tools then you can't really complain that your screwdriver is rubbish at hammering in nails. And it doesn't matter how easy or hard any programming language is, all languages will have a learning curve that people need to be prepared to undertake.


It's not that I don't want to, it's more I don't have the time to invest upon. I'd read all the national library books if I could, but I just don't have the time.

End of shift, I walk away from my screen and try to do something other than. I'm burnt out enough by the tech field as it is.


It seems to me that the problem here is your job, not Go. You could substitute Go here for any other language or technology that you don't know well and for which you're not given adequate time to learn the basics of, and you'd get the exact same result.


My job is solid. I have time to do such. I would rather just use my time available on have more pressing issues then a language I don't really want to use.

Sure knowledge is boss, but my job is fine. It's not the job at fault.


So it seems clear to me? You don't want to use the language, you don't want to learn the language, no-one is forcing you to use the language, then don't use the language.

What are your expectations? Is it that every language should be readable to people who know Perl? Is it that you shouldn't be reading or modifying the source code of open source projects? How could this work better in an ideal world? I am puzzled.


Why are you turning this in to such a pedantic thing? I am not a programmer. I am a systems administrator who administrates systems. Is that not easy to grasp?

When my job is 98.9% something else than programming, I'd rather be learning AI, Robotics, History, Psychics, Gardening, Glass Blowing than a language I'll never use.

I've explained it a case of "Can you get this to work" Yes/No. If no, I move on to something else. That's my line of work.

If you want me to monitor,configure,administrate something, then it should be simple. Presented to me in one folder with all packages not downloaded from some corporate hell-hole of a website because it's the norm for younger developers. Am I not allowed to have that opinion?

I will research on how to operate but that's it. Life's busy enough that I don't have time to further research on how to setup individual environments. I've just wasted 10 minutes typing this message to reply rather then configuring an openldap server for user authentication, Luckily I'm on lunch.


I'm not trying to be pedantic. Please do not assume bad faith.

I'm just trying to understand that worldview, which just seems drastically different from mine, even though we both seem to be generalists. If there is something that Go, or any other product, could do better to be better suited to your workflow, I would like to know what it is.

I don't write Perl, but if I had to qualify some piece of software that used it at work, I would make time then to learn as much of the basics of it that is needed to more easily get by. Even if it meant a day of reading Perl/CPAN docs to do 15 minutes of actual 'work', especially if the alternative is one or two days of blindly stumbling around to then not even be exactly sure if my 'yes/no' answer was correct. I don't even see much of a difference between learning something and just working with it - I can't imagine not having a browser window open with the docs/source code/whatever of what I'm currently working with.

In that way I've learned a fair bit of technologies I would have never touched otherwise, but now their knowledge, even at a surface level, is worth more than the sum of the products.


I mean no offense and sorry for my angst if it came that way. It's frustrating when I can't paint my view and then get battled to death because of it.

TCL.

TCL is the only language workflow that has worked for without any stress.

    proc SomeProcedure {variable} {
     Code it to do something for yourself }
save, run and it works. No need to include modules, no need to download anything. I use my own TK/Gui interface which does stuff for me to make my life easier. And if I handed it to you, it would be in one folder with all files present no dependencies required. However I understand life is not like that unfortunately.

> I can't imagine not having a browser window open with the docs/source code/whatever of what I'm currently working with.

I don't. If at any point I need source code, I'll download it locally and compile/run it first. Then edit it learn the logic and flow of the program. I'm in terminal 99% of the time, orchestrating systems ranging from FreeBSD to Linux and AIX, if a browser is open it's for Slack, emails.

I am someone who wants to type "man something" and get a man page of it. vim Library/file.pm and be able to view the code under the hood for that specific project. That's simplicity and that's how it should be -- The over-hassle of downloading from git, forking it, apply push,pull changes just isn't my street specially assuming my box I'm working with has internet.


The issue is you presented opinion as fact.

Opinion: I don't enjoy Go as much as Perl. [PASS]

Opinion: I'm more comfortable with Perl because I've spent more time in it. [PASS]

Both are perfectly acceptable.

However you made comments about objective truths that were incorrect:

Truth: Go doesn't download modules to disk. [FAIL]

For what it's worth, I really like Perl too. Us two are a dying breed in that regard. I was in two minds whether to reply because of that respect I have for fellow Perl hackers. But you kept repeating the same falsehoods as fact :(

Take your latest comment for example, you can bring up the Go docs for packages in the terminal as well:

    go doc $PACKAGE
So even there your Perl point is still equivalent to Go on an objective level. But I don't blame you for not liking/enjoying a language on a personal level.


My original discussion was of that I thought Go was very hidden under the hood. I gave an example and got mobbed for it. I replied with emotion and misunderstanding and here we are.

The main thing I was going on is "Here's a GitHub link, add it to your code and you have an instant logger thing". Magic in my eyes and this blinding light throws me off.

Unlike Perl where the code is in a .pm file in front of you. That was what I was trying portray.

I'm going to leave this discussion to rest as it's all went wrong anyway. Tomorrow will be tomorrow and this will all be forgotten in a day.

Thank you for your responses and have a good evening.


Okay so you don't want to spend time learning new stuff but at the same time you don't want to be left behind.

You want a stagnant field. Sorry if I come across as too blunt, but you chose the wrong line of work if you don't like change. You'll still be able to work with the same old, boring but proven tech for the rest of your life, but face the fact that you'll be more inconvenienced by the reality you live in as time passes.


What? I want change, I like change. I understand change and I will learn change.

However when I have more pressing matters and when I am not requiring to use Go for my day to day life I have no purpose to put further education in to it other then enough to get by with.


You can import them at any time. Most people import early so that IDE's can inspect the packages (eg for completion tools etc).

Again, just like with Perl.


That's no different to importing a module in Perl. If you want to know what's in that module then you read the source of it.

> I understand all of that. Power_state, display the help, power status description etc, but prometheus.NewGaugeVec -- great. What is it? Where do I find this?

It's a function in the module `prometheus`: "github.com/prometheus/client_golang/prometheus"

It's the same thing as the following Perl code:

    use Prometheus;
    use Prometheus::Log;
    use Prometheus::Find;
    use Prometheus::View;
    use vmware::govmomi::vim25::mo;
    use mware::govmomi::vim25::types;

    my %prometheusHostPowerState
 = prometheus::NewGaugeVec(prometheus->GaugeOpts{
        "Namespace"  => $namespace,
        "Subsystem" => "host",
        "Name" => "power_state",
        "Help" => "poweredOn 1, poweredOff 2, standBy 3, other 0",
    }, @("host_name");
As you can see the code is actually really similar between the two languages in that regard. Bare in mind I'm not suggesting that the languages themselves are similar, eg how modules are created, methods written and arrays are handled are clearly different. But the concept of reusable imported modules and properties in a struct/hash are ostensibly the same.

Disclaimer: I've not written much Perl in the last 3 years so I might have gotten some of the syntax slightly wrong. But it should be close enough for demo purposes.


Then it would be fair to say I can't grasp the new-modern use of github module method then the traditional where prometheus.pm exists on disk and I can trace,trap and edit on the fly.

With the line above, prometheus::NewGaugeVec. I know it's related to the Prometheus module, NewGaurgeVec subroutine.

I'm not a programmer, I'm a systems admin who job is to take requests of such "we need a vmware exporter, I have this. Make it work".


The "new-modern use of a github module" is the same as for Perl. If a module isn't in CPAN you have to manually download and place in your project directory. Go just does this for you. And the module files in Go are on disk -- there's no way you could compile the project if half the source code wasn't on disk.

I think you'd probably have enjoyed Go more in the "old" days when $GOPATH was required and all modules were downloaded into your $GOPATH as it was more explicit were modules were and in a way it felt a little more like Perl with the file system being a more prominent part of modules. This was a very unpopular pattern for developers so things have moved on somewhat with modules now being pulled into a package directory. Personally I preferred the $GOPATH days too but it's fair to say the modules are still on disk.


You do not need to go down the Github rabbit hole. The symbol prometheus is from the package github.com/prometheus/client_golang/prometheus. Use go doc github.com/prometheus/client_golang/prometheus to read the documentation from your local copy of the package. Use go list -f {{.Dir}} github.com/prometheus/client_golang/prometheus to find out where your local copy is located. If there is only one prometheus.NewGaugeVec in your workspace, then the command go doc prometheus.NewGaugeVec shows the documentation for function and reports the package where it's defined.


Yes, it states the obvious, but I'm trying to point out what I mean. It's not always so golden as such.


godoc, same problem with any language that uses any kind of modules, you need to consult the documentation for that module. https://pkg.go.dev/github.com/prometheus/client_golang/prome... .. if fount that by googling `godoc prometheus newgaugevec` .. or you could just use `godoc`

Your argument seems to be, "I don't like reading documentation." Then by all means don't and write your own prometheus metrics module shrug


> Then by all means don't and write your own prometheus metrics module shrug

I'm a sysadmin, not a programmer. So I don't code for business operations. I compile them to make them work. I don't have time to read all the schematics of the application to make it work, but when it comes to perl, or TCL for that matter. All files are local, if my box has air-gapped and no access to github. Then what?

> Your argument seems to be, "I don't like reading documentation."

Not at all. I have limited time to spare on documentation. It's easier for me to read the code in front in practical form then it is to read documentation written on some wiki.


`go mod vendor` will populate all dependency sources into `./vendor/` in a modules project for easy grepping.

Some projects still use this to ensure sources work airgapped as well.

https://golang.org/cmd/go/#hdr-Make_vendored_copy_of_depende...


> I'm a sysadmin, not a programmer. So I don't code for business operations. I compile them to make them work.

Sounds like you have an organizational problem, not a programming language problem.

I would expect developers to provide you with an actual application or at least automated way to build it, and you to deploy and handle the given application.

Instead you have developers providing source code and you trying to understand how to make it work.

There are plenty of tools, methods and ways to get a repeatable build process, including docker, CI, bash scripts...

Even a README file can contain a valid and repeatable build process.

Even a process called 'ask to Robert how it works' would provide you with the information you need

It's hard to imagine that this is not scripted or at least documented somewhere.


To me, compared to other languages, I find Go easier for expressing my ideas in code. People seem to argue as if there was an objective truth. Different people may have different learning styles and working requirements. I'm not programming full-time so I prefer a simpler language so I don't forget. A full-time programmer may want to invest in a more "complex" language. See, you are all right.


When people say Go is a simple language, they mean that Go, the literal language, is simple. Some people are also claiming that the programs written in Go are simple, that the ecosystem is simple, or that using Go is easy, and those people are probably wrong. However, Go the language is actually simple.

Here is the Go spec: https://golang.org/ref/spec. Firefox's print dialogue tells me it's 105 pages long. You can read the spec in an afternoon and digest most of it.

Here is the ECMAScript 11.0 spec (known as JavaScript to its friends): https://262.ecma-international.org/11.0/. Firefox's print dialogue tells me it's 1181 pages long.

Here's the Java SE 16 spec: https://docs.oracle.com/javase/specs/jls/se16/jls16.pdf. It's 844 pages long.

I can't post the C++20 spec because it's an ISO standard and it costs $399AUD for the privilege of reading it, but it's 1853 pages long in PDF form: https://www.iso.org/standard/79358.html.

On a factual level, Go is a simple language, but so is the language Brainfuck, which has a sort-of-spec that's 9 pages long: https://github.com/brain-lang/brainfuck/blob/master/brainfuc.... Simplicity is not ease of use.


Well, to be fair, the Go spec is missing several parts of the language, such as finalization, thread safety (the memory model, sync points or other happens-before relationships), goroutine memory/stack usage, anything to do with go modules.

Still, even if they added these, it would likely stay significantly shorter than even Java.


> finalization, thread safety (the memory model, sync points or other happens-before relationships), goroutine memory/stack usage, anything to do with go modules

I dont think it affects your point but those are implementation details rather than features of the language itself. Obviously it's still important to understand the language implementation so you can make reasonable guesses about performance etc. when you're programming.


With the exception of goroutine memory/stack usage perhaps, these are not implementation details - they are core parts of the semantics of the language (especially since asynchronicity is a fundamental aspect of the language through goroutines).

For example, is this piece of code correct?

  x := 7
  go func() {
    if x == 7 {
      fmt.Println("x = 7")
    } else {
      fmt.Println("x != 7")
    }
  }()
  x = 8
What are its possible valid outputs? (for the record, as far as I know this is incorrect code in Go, as atomic access is not guaranteed for any variable, so not only could this print any of the two outputs, it could also observe invalid values for x)

What about this code:

  x := 7
  c := make(chan struct{}, 0)
  go func() {
    if x == 7 {
      fmt.Println("x = 7")
    } else {
      fmt.Println("x != 7")
    }
    c<-struct{}{}
  }()
  <-c
  x = 8
Can you tell from the language spec whether this code is safe, and what it's output will be? (~~it's not safe, as far as I know~~ reading the memory model[0], I believe it is safe).

[0] https://golang.org/ref/mem


To be fair, Java’s memory model was also pretty broken for several years.


The point is that other language specifications do include these things, which makes comparing their length to the Go specification inaccurate.


> When people say Go is a simple language, they mean that Go, the literal language, is simple.

Perhaps. But what use is a language that is simple if it doesn't make it simpler to use or easier to learn? I think most people who claim that Go is simple are claiming the latter, because the former is mostly uninteresting. It's good for IDE support I guess.


Pencil is a simpler medium than oil paints, but that doesn't make it any easier to do a good portrait with it. Saying its a simple language is expressing a trait of the language that some people may like or find they have an affinity for. People seem to get stuck with the idea that programming languages can only be compared via objective criteria where as we seem to use these sorts of signals to help each other find mediums we like.


It's a tradeoff. You get a simple language without a lot of surprising features, but sometimes code is very verbose; sometimes problems that would be trivial in other languages require unfortunate solutions like cgo, reflection, strange hacks[0], or code generation; and sometimes the "sensible" opinionated ecosystem doesn't fit how you want to work.

[0] https://golang.org/pkg/sort/#example_


I'd point out that ES5 spec contains the core of the JS language in 258 pages.

Following the JS spec alone, it would probably be hard to find any code on the entire internet before 2015 that wouldn't run on your implementation (even though it would undoubtedly run slower).

In contrast, if you followed only the go spec, ZERO go code out there would actually run because it doesn't include the rest of the language and core features.


The C++ standard includes the standard library.

If you want a fair comparisasion, include the standard library for the other languages.


Working from a freely available draft of the C++ 2017 spec: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n465...

In total, it's 1622 pages. Without the cross-references and the index it's only 1486 pages. Also excluding the standard library, it's 520 pages.

There, the fair comparison is:

    Go:    105 pages
    C++17: 520 pages


Sure, thanks for putting in the effort.

By the way, Java, Python and .NET ones top the C++ one.


Eh, that's a bit problematic — there's no standardization in what's in a standard library.

Some languages have stdlibs that just contain "stuff everybody needs" — stuff every app or library written in the language will use (common data structures, concurrency primitives, etc.), such that anyone who "knows the language" would also be expected/required to know (or at least know of) those components, if they have any hope of being productive in the language.

But other languages have huge stdlibs, that include a bunch of stuff that almost nobody is even aware of, such that most developers won't actually need to know about it in order to effectively maintain other people's code in the language. These are essentially just weird niche libraries that happen to ship with the runtime for some reason.

Examples of weird niche libraries that are technically "stdlib":

• PHP has an Ingres driver built in. That's Ingres, the predecessor to Postgres.

• Ruby ships with a CJK character-normalization CLI utility.

• Erlang/OTP has "Megaco" (http://erlang.org/doc/apps/megaco/megaco_intro.html) — an H.248 media gateway daemon library.

Admittedly, languages do tend to gradually factor these weird niche bits out into separate packages. (Ruby just moved almost all of its stdlib into ecosystem packages in 3.0. Some ecosystem packages are just now install-by-default — but maybe won't be in the future.)

Also, the behavior of these weird niche bits is not usually specified as part of the language's spec... but that doesn't mean much, as often, these languages don't even have specs. In those cases, the reference implementation is the spec — and so, if the reference impl includes these libs, "the language" includes these libs.


> Ignore comments about how go is simple to write.

Do people say that? I've never heard anyone claim that Go is simple to write.

Pike, I believe (may have been someone else), claimed something to the effect that new grads with limited exposure to real-world codebases were able to understand existing projects written in Go more quickly than in other languages at Google. I have seen that message distilled down into "Go is simple to read."

Go was built on the premise that people spend more time reading code than writing code and Go attempts to make tradeoffs towards reading optimization rather than writing optimization. Whether or not it is successful in that is another topic for another day.


Go is very simple to read and to write, I'm not sure how one can argue against that.

Without prior knowledge you can jump into any code base and won't be suprised by something you never seen before or does not understand what it does. The language is simple to grasp because it has just a few concepts and keywords.


If by "read" here we mean "grok" — i.e. to read and understand well-enough to be able to modify without breaking the abstract guarantees the code makes, without documentation — then I would say that the low level of Go's concurrency primitives (relative to other languages modelling the same actor-based architecture, e.g. Erlang) means it takes more effort to "read" production-quality (concurrent, fault-tolerant, no resource-leaks, no data races) Go code relative to production-quality code written in other languages.

Sure, when reading Go, you aren't really encountering new language features; but you're often being forced to recognize things that should be language features, which are instead implemented as design patterns. (And, at that, often with those patterns reified into code by new-ish programmers who don't understand those design-patterns very well, rather than by language maintainers after much careful scrutiny.)

It's like doing OOP in C, and having to mentally parse vtable structs into the object-model they represent — with every project having done its own vtable implementation, all slightly different. Except that OOP is unusual in C, while concurrent infrastructure is near-universal in Go.

When reading Go, I'm essentially mentally "decompiling" it as I go into a higher-level representation within my mental-model. "These 50 lines are a parallel map-reduce; these 200 lines are a read-through cache with per-key synchronization to avoid duplicate work; etc." I'd rather just read that higher-level representation directly!


As someone who cut their teeth on programming via C and c++ Go did come surprisingly easy. My first interaction with it was a small change to a web thing and I had no trouble reading source code, understanding it, and making the necessary changes without ever learning to write a line of Go before. That alone is value.

To me Go feels like a "modern" C language from 90's Bell Labs. Of course that is kind of true as Go is the spiritual successor to Alef and Limbo. Though it feels like it came out 10+ years too late.


> To me Go feels like a "modern" C language

That was the stated intent at the time Go was envisioned. They missed the mark a bit by not making Go a systems language, but oh well.


Yes, Go is what Java 1.0 should have been, in 1996.


That was supposed to be Inferno/Limbo but sadly it didn't happen.


Yes, not only it did not happen, most people aren't even aware of it as they keep praising Plan 9 instead.

In any case, Go is Limbo with a bit of Oberon-2 facelift, and as such a bit too late to the party.

At least they might finally support some level of generics and catch up with CLU.


Because plan 9 was developed naturally in the Lab as an evolution of Unix research.

Inferno on the other hand was a blunt force attempt by Lucent to leapfrog Sun by halting Plan 9 development for a whole year forcing the team to instead work on Inferno. It was a commercial project to produce a product which was in stark contrast to the more academic/research oriented development of Plan 9.

Unfortunately, this hamfisted attempt by Lucent left Inferno in a state of Limbo, pun intended. The target was more along the lines of embedded systems like set top boxes and early PDA's. The Dis VM also suffers from hard coded 32bit pointer magic which means it only builds on 32bit plan 9 or in a 32bit chroot under a unix-like for hosted mode. And finally, the Charon web browser has a bug that crashes the Dis VM. I have yet to see it boot on bare metal hardware in its entirety. These reasons are why Plan 9 gets all the love, it works out of the box on 386/amd64 and arm32/arm64 (Rpi, Zynq, etc)

Though not all is lost. Someone is working on getting it to build on 64bit 9front and OpenBSD, though the code is very much a WIP (if interested, hop into #cat-v on freenode and ask about the Inferno 64 bit port). It's not completely dead. Patches welcome :-)


Interesting background history, do you have some pointers to read more about such stuff regarding Inferno?


I believe that info came from a plan 9 user who interned at Bell Labs in the 00's. Though honestly you can get much better answers from the source by posting to the 9fans mailing list. Charles of Vita Nova, current owner of the Inferno code is a regular on that list as are many of the original Bell Labs folks.


Thanks


it's simple to write ONCE you understand and learn the idiomatic code.

some people call not having native string/int/whatever sorting methods is "hard to write".

some people think the verbose `if err != nil` every other line is also "hard to write"

again, there is an upstart cost, which is to integrate yourself into the language community.


Golang does have a few simple types sorting functions such as string and int https://golang.org/pkg/sort/#Strings. Now that generics will be out, could we even have generic sort functions ?


you cannot write a generic function that returns a modified slice without losing types. that, in my book, means hard to write.

...which makes it also very easy to read.


I don't see the point of cherry picking highly specific examples because for every argument there will be just as many counterarguments from other domains.

To be more specific: the point isn't whether Go is "always" simpler to write but whether it is generally simpler to write. I think the latter is true for all the domains I've written code in Go and I've solved some pretty hard problems in Go (check out my Github repositories if you don't believe me).

The awesome thing about programming languages is that you're free to pick another language if that is a better fit for any specific domain or even personal preference. Hence why I've worked in more than a dozen different programming languages over the years. So rather than moaning that Go doesn't fit a particular pattern where you need to rely on generics, perhaps use a language that has generics to solve that problem and save Go for the occasions you don't need generics (which, if we're all honest, isn't something we need to rely on half as much as people claim when moaning about the lack of features in Go).

As a veteran developer, what really annoys me the most is people who pick a language that isn't well suited for solving the problem at hand and then moan that language needs to change, rather than developers picking the right tools for the job. I'd rather see a language lack features but focuses to serves its core demographic well than see everything and the kitchen sink is thrown at it and everyone using it even when they know they shouldn't simply because they're too afraid to learn anything new. What we need is more appreciation that "general purpose languages" doesn't mean "should be used to do practically anything in". Then I'd think we'd all get along better.


I meant it as a random example. I do not dislike go. As I've stated below, I actually am learning it (more like, the conventions around it), just for fun.

I find it especially powerful for writing tools.

That doesn't remove my rights to nitpick its shortcomings though.


I've been writing Go full-time, professionally for around 4 years now. Backend software, mostly distributed stuff, tools and services - the bread and butter of what Go's designed to be good at. On the side, I occasionally write some Rust, Haskell, and other 'good' languages.

I have kept a counter of many times I've told myself, over the past years, 'I wish Go had generics, this would really make my life easier right now'. It's currently at 5.

In my experience, the supposed issue of a lack of generics is _extremely_ overblown with regards to real world use. Sure, it would be nice to have them. I would've really liked them to exist a month ago when I was writing a generic container. But they're not going to make the language that much better for me, and there's always going to be more things PL people lust after (usually the next candidate would be GADTs, “Go does not have algebraic data types!” coming to HN comments very soon).


As a counterpoint: I also write Go in this same space. I like Go - I find most of its design goals align closely with my personal preferences (avoidance of syntactic noise, fast compile times, right-sized standard library, sufficient control over memory without having to worry about memory safety).

If I kept a similar counter for the past 3 years, it would be at least five per day.

Every time I `return x, err` and it's not a true tuple - and it almost never is - I wish I was returning an `Either<X, error>`. That's constant.

Every time I use atomic.Value (often) or sync.Pool (less often) or sync.Map (almost never because I end up rolling my own stupid map+mutex).

90% of the time I want to say a pointer can't be null. 100% of the time I want to handle optional JSON or SQL fields.

Fairly often, like > 30% of the time, when wrapping serdes or DTO conversions around data streams.

I don't believe the level of generics required to support these use cases requires compromising the other goals of Go. The deficiency should have been obvious as soon as they introduced `sync.Map`.


>Every time I `return x, err` and it's not a true tuple - and it almost never is - I wish I was returning an `Either<X, error>`.

I can't imagine having this thought every single time. I don't want to sound harsh, but I don't know how else to ask this: can't you get over it?


Turning off my "how should I design this code well?" filter would probably remove the little satisfaction I still get from my day job. Coworkers and customers would likely not be too happy either.


But it's never "well I don't have Either available, I'll work with what I got"?


I don't understand how "I'll work with what I got" (which I do) is incompatible with wishing I had Either (which I also do).


It just sounds quite painful to me.


> there's always going to be more things PL people lust after

How about non-nullable types?


Sure, put that on the pile labeled “things that sometimes bother me, but I forget they're even an issue when I talk about Go with others”, next to generics.


I would argue that it only makes it hard to write type safe generic functions that return modified slices. If you're writing generic code for the sake of writing generic code, Go is probably going to give you a bad time, but if you're trying to solve real problems, you can get a very long way with slices, maps, and interfaces/functions.


filter/map/reduce are more productive than anything I could write without them.


I’m very skeptical. This kind of argument only makes sense if your programming is bottlenecked by your typing speed. It’s trivial to write these functions as for loops, the only difference being the number of key presses.


filter is obvious at a glance, where all loops look similar. Generating a wall of boilerplate is easy, the problem is that we can’t just ignore it and read a summary. Essentially I’d like to decompile Go to what could have been written in a powerful language.


Wall?

    var out []X
    for _, x := range elts {
        if condition(x) {
            out = append(out, x)
        }
    }
That’s so easy I typed it from my iPhone and anyone looking at it can infer at a glance that it’s filtering a list. If you are deeply worried about it, throw a comment at the top of the block.

I too like terse code, but it’s because it makes me feel smart, not because it’s easier to read or more maintainable. I’m having a blast writing Rust for my hobby project, making everything super DRY and free from boilerplate, but I don’t pretend that I’m saving myself time or making things more readable or maintainable should someone else come across it.


elts.filter(condition(x)) way more readable.


In my opinion the two are very comparable. If filter() is more readable, it’s not by much, and this is all micro optimization bike shedding. I’ve never had a hard time using the idioms in a given language, and things like map/filter vs for loops were never what pushed me away from or pulled me towards a language. The important things are almost always learning curve, tooling, ecosystem, performance, maintainability, etc and Go does very well in many of those respects. The actual in-language features are generally just gravy.

Note that the Python people have map/filter/reduce (and have had them for ages) and no one uses them. It’s either for loops or list comprehensions, and people notoriously struggle with the latter.


That's because in Python comprehensions have better ergonomics than lambdas. Scala has comprehensions too

  for { i <- 1 to 10; if i % 2 == 0 } yield i
but sometimes lambdas can be cleaner

  1 to 10 filter { _ % 2 == 0 }


The limitation to lambdas is just the single expression, but the same limitation exists for comprehensions. Comprehensions aren’t particularly readable beyond one loop and a condition or so (I say this begrudgingly as someone who likes to craft complex comprehensions). Beyond that for loops are encouraged, not filter() and friends, which suggests that they are perhaps not as amazing as some in this thread suggest.


Only if you know what a filter is and how the condition is parsed.

In more cases than one the condition is a lambda-monstrosity shoved in the filter function, which makes it even harder to parse.


In the situation, even for statement also become monster.


Not if you're not used to it. Literally had this over the past few months working with a junior programmer who had a really hard time understanding stacked map()s and filter()s but understood them easily when I unrolled them into nested for loops.


The junior programmer would have been much better off learning how a filter function works so they could be productive in almost every other language besides Go.


This was Javascript. And yes, I was teaching him how filter functions worked. But the point remains that if you're not used to them, map() and filter(), especially when stacked, are not that readable.


Then you can go ahead and use one of the many code generation libraries out there to handle it for you in a type-safe way.

Here are three options that were on the first page of google:

* https://github.com/kulshekhar/fungen

* https://github.com/awalterschulze/goderive

* https://github.com/cheekybits/genny


I think the word you're looking for is "tedious". Go is both simple and tedious to write. I'd actually argue it's tedious _because_ of its simplicity.


But all that boilerplate also makes it hard to read. You have to pick out the meaning amongst a bunch of noise.


Not once you're used to it. Humans are good at spotting patterns, and once you're used to the patterns of the boilerplate it becomes easy to read and you only notice the anomalies.

The famous "if err != nil {" boilerplate becomes expected after each call and you only notice if it changes.


Then you write 2 functions, what you're talking about is a different question.


if having to write a separate function for each type of container is easy for you, then I can only respectfully disagree :)

I'm saying this as someone who is learning go for fun.


Maybe. If you have to remember two functions to _understand_ the code, is reading really easier? I imagine reading implies understanding, and while i definitely think Go is easier to read without understanding, i don't think that's the real implication.


> Pike, I believe [...], claimed something to the effect that new grads with limited exposure to real-world codebases were able to understand existing projects written in Go more quickly than in other languages at Google. I have seen that message distilled down into "Go is simple to read."

Yeah, it was Pike, and I have also seen it distilled (in bad faith) into "Go is a primitive language for inexperienced programmers, so it's beneath me to touch it". And yes, it is easier to write and read compared to other languages (old and new), but it does have some concepts that you should follow to write "idiomatic" Go code, e.g. don't throw panics as liberally as you would throw exceptions in other languages, use composition and interfaces where you would use inheritance in other languages etc. But I'm afraid people who have been indoctrinated with disregarding Go as "simple", but want to try it nevertheless (or are forced to use it), won't take the time to get into these details, and will see their preconceived opinions confirmed...


> I have seen that message distilled down into "Go is simple to read."

I think the two are related, directly. However in my experience i don't even agree with Pike. Go is not easy to read. Yes, a single small function in Go is much easier to read (perhaps) than X lang. However Go requires a huge (brain)memory table to remember everything, because it spreads logic out. So you have to remember X function, scroll up, look at Y function remembering how it interacts with X, etcetc.

> Whether or not it is successful in that is another topic for another day.

I guess i bikeshedded, sorry :s


> Do people say that? I've never heard anyone claim that Go is simple to write.

The article we are discussing now has a big subtitle "A simple language."


The complexity is about the bare minimum to express concepts like concurrency, pointers, and interfaces, in my opinion. The ways Go seems complex are because it doesn't try to hide how memory works as much as other memory-managed languages, and in turn, as an engineer, you can write application and web-level code that performs almost as well as C and gets you thinking about things like a C programmer. I think Go just seems familiar to people that come from a variety of languages.

For me, the simplicity, batteries-included, and minimal primitives are a breath of fresh air, whereas in many languages I've felt like I have to learn "a whole new thing" each time I plug in a new library or try a new framework. Go has a web server, JSON, SQL, crypto, compression, and more built in, and it's all stuff you'd want to use directly in production. In Go, code and docs are just easier to write and navigate too. Compare, for instance, the prevalence of Reader/Writer types to how all-over-the-place implementations of buffers and asynchronous code are in other languages and ecosystems, or how many different build systems, dialects, and frameworks most languages have. The locality of most Go packages means you'll find better docs and help, and more canonical examples of how to do something.

Swift is another "deceptively simple" language to me. Swift has quite a few features, which means more prerequisite knowledge to read code "in the field". You're less likely to understand code by looking at it because there's more ways it can pan out while the developer is thinking about how to write it. I found that to be an especially big problem with JavaScript; I generally tell people to start writing JavaScript with a particular toolchain like React/TypeScript so they can set up a project and get on the right track fast. Go's ecosystem is opinionated but still just flexible enough, as I would say most of the features are.


> OTOH, things like criticisms about it's error handling are overblown. At best it's a mild nuisance, and more often than not, a good thing that makes error handling explicit and clear.

Yea, i'm a bit critic of Go (used it professionally for ~5 years, but no longer), but it's error handling is fine. Would i like Rust's `?` error handling syntax? Yes. Would i prefer better language tools for error handling (like enums/etc) rather than usually just wrapping strings? Absolutely.

But the actual _need to error check_ itself is fine. I think it's funny, because to me the Go audience is one similar to NodeJS/JavaScript, aka one that favors features to improve prototyping. And while much of Go feels (at first glance) to promote this, the error handling does not.

I prefer Go error handling. But i wonder if Try/Catch would have been more inline with much of Go's audience.


VSCode still works fine with Go, but I do agree that Goland is better. Gopls[0] (the Go language server) has definitely been getting better, but it was not-so-great for a while (it locks up from time-to-time and you have to restart it, but that may be better now).

[0] https://github.com/golang/tools/blob/master/gopls/README.md


I moved away from VSCode for Go simply because code completion was taking forever or sometimes failed to work at all. I believe that may hav e been related to gopls? Have others experienced this as well? Is that fixed now?


In the command menu for VSCode (ctrl-shift-P on windows), there is an option called "Go: Restart Language Server", which tends to fix any problems with autocomplete or code navigation.

I feel like I haven't had to use it as much recently.


Yeah I dealt with that for months, but it has greatly improved.


In my experience, Go is as simple as you make it and it is easy to learn. You can cover the whole language in a few hours. With anything that simplifies interacting with the underlying hardware there will be complexity hidden away from you. That's inevitable. Go is no different to any other language in that regard.

The hardest part of Go is learning how to effectively use things like goroutines and channels.

Slices might catch some noobs out, but at the end of the day it's as simple as saying "a slice is a view on a block of memory", so 2 slices can point at the same data, and changing data with 1 slice can be reflected in the other. Once you realise that a slice doesn't hold values there's no issues. That's simple, I just explained slices in 5 seconds.

I think implicit interfaces are amazing. Yes it can be hard to figure out what implements it, but an IDE is used for that. Like you say, Goland is really good. Abuse of interface{} is bad, it's the worse decision that was ever made for the language in my opinion. But I don't think it's that big of a deal in the grand scheme of things. Generics will remove a lot of it's current uses anyway. I write microservices and the only time I have to touch interface{} is when using contexts.

It's not for everyone, but people saying "Go isn't simple" are just as bad. You've got to understand people from all sorts of backgrounds have learned Go. For me, I really think it's easy to learn and simple to use. Others might think it's the hardest thing they've ever done.


Ahhh Context.

For me, using context and stuffing and retrieving things from it is the most painful part of go.

If anything from go should die in a fire, it’s Context - well, the key value storage anyway. The canceling part is fine.


Context is really good so long as what you're storing deserves to be there. For example, any UUIDs you generate for request tracing or anything like that. Once you start storing actual request data like users, or session data, etc. then it's a problem.

I'm of the opinion that (if you're writing web services) anything related to the request should just be passed to the function using it. Shouldn't be stored on context and passed along 20 middleware handlers. If you need to share state this way then it's a failure of your design. Putting the meat of your business logic in multiple handlers is not the way. A handler is how you get the data into your application, then you can forget about them. But some people don't really understand that and start having authentication middleware, session middleware, user middleware etc. Just pointless complexity.


For you the problem is the very fact you're able to pass values through context transparently or that some people abuse it passing all sort of data that should be passed in better ways?


If something is so frequently abused to become normal, it means the design is somehow wrong.

Design of context and the key/value storage there is wrong.


> Yes it can be hard to figure out what implements it, but an IDE is used for that.

Which is ironic given that the golang designers and users tend to shun IDEs and mock that languages like Java rely on them. When in fact, to do anything useful in golang you also need an IDE.


I tend to agree with you and had a similar experience. I found a lot to love about Go, but also found things which were just hard to fathom or reason about. For example, ask a Go programmer, given an interface, how you are supposed to find all implementations of that interface. As of a few months ago, there just wasn't a good answer for this, although supposedly you could get there with gopls if you knew how.


`M-x lsp-find-implementation`.


Go is simple in the very same way that Tetris is simple. It can be taught quickly, it is easy to make mistakes, and when manipulated by a skilled operator, is absolutely beautiful.

Other languages are simple in the same way that landing a rover on Mars is simple: not simple at all. Every caveat I listed for Go and Tetris also applies to these other languages.

So, all other things being equal, Go's simplicity wins, for me.


I think it would be a more productive discussion to ask whether go is a simpler language (compared to popular alternatives), not whether it is simple in an absolute sense.

Given how complex computers and programming is, it's easy to argue that any language is not simple.

It's a more interesting question to ask which of the languages is simplest.


This really matches my experience, in that learning the Go language is very simple, but learning to correctly use the Go language to reliably build correct programs is much more difficult. The Go compiler (and other tooling like go vet and the race detector) gives me very little assistance or confidence in my work. The Go standard library is similarly very weak, leaving me to repeatedly re-implement things that are taken for granted in other languages, like common collection operations.

One big example is Go slices, which are "easy to use" and "just do the right thing" up until they don't. If you append to a slice, it might or might not allocate a new backing array depending on the slice capacity, so whether your function is mutating an original or not can suddenly change based on changes in other parts of the code, and it's a huge pain to track down bugs like that.

Go's channels are "Easy to get started with", but very difficult to correctly compose, or do anything nontrivial with. Reading from a closed channel will "successfully" generate zero values instead of failing to read a value. You have to replace your variables with a nil channel instead.

When writing Go, I feel like programming by coincidence. With a lot of careful thought and work and testing, I can arrange for something that works, but it's fragile under refactoring and maintenance due to spooky action at a distance.

None of the stdlib collections are threadsafe, but if you mistakenly share one between threads, it works fine up until you hit sufficient concurrency to have multiple concurrent mutations.

Go is "easy" due to garbage collection, but that only handles memory, and offers you zero assistance in handling any other resources. There's no destructors, and no RAII, so you still have to carefully manually manage resource cleanup for files, database connections, locks, mutexes.

Go's type system is too weak to express a mutex owning its guarded value, so it's left up to careful code review to ensure that the guarded value is only accessed while the mutex is locked.

I feel like "the language" is simple, sure, but to successfully use it, you have to also learn many little constructs you have to repeatedly type out, and techniques to protect yourself from all the sharp edges.

For me, the Go Language was faster to learn than the Rust Language, but learning to reliably build correct programs with Rust was way easier than learning to reliably build correct programs with Go.

I like Rust because I am a mediocre programmer with poor working memory. Rust lets me build abstractions I can rely on, and reason locally in small parts while working on a program larger than I can easily fit into my head.

Go makes some operations "easy", but unfortunately also makes shooting myself in the foot much easier than doing something safe and correct.

The Go language itself is very simple because it abdicates all responsibility for correctness, leaving it entirely up to the programmer. You still need to handle all of the same responsibilities regarding ownership, resource lifetimes, locking, thread-safety, error-checking, exhaustiveness-checking, etc. but Go leaves all of that up to individuals to learn for themselves through trial-and-error, and ignores all advances in PL theory on how languages can assist with these problems.

I could understand some of the appeal of Go if it was people saying they like to use it for half-assed one-off scripts that don't grow to large sizes and don't have any long-term maintenance, but instead I keep seeing people build large production systems with it.


> but learning to correctly use the Go language to reliably build correct programs is much more difficult

I feel this strongly as someone who prefers to write code which always works versus the "works except on edge cases" approach of most Go code. It's a step up from bash though.


Simple vs Easy rears its ugly head again.


> If anybody wants to learn go, ignore everything about how easy it is to learn. Because it's extremely deceptive. Take time to actually read through a lot of the important underlying concepts like how slices work.

What is your standard for "simple" if learning how to use slices is complex? Go slices are just windows into contiguous blocks of memory. There are pitfalls--if you have two juxtaposed slices backed by the same contiguous block of memory, growing the former will write over the latter, but you quickly learn that you should virtually never be appending to slices that don't have sole ownership over their backing memory. Probably the worst part is that there aren't higher-level functions for prepending or deleting an item, so you have to build them yourself with append, copy, etc primitives (but even this has the nice property of exposing the cost of these operations, e.g., deleting from the middle of a slice is expensive and your average Pythonista probably doesn't understand this).

> Ignore comments about how go is simple to write. There's a fair amount of bad go code and people break rules all the time. From official sdks to standard libs. Go can be extremely difficult to both write and read.

This hasn't been my experience.

> implicit interfaces

I love structural sub typing ("implicit interfaces")--I can define an interface for a third party type which in a nominally subtyped language would require wrapping the third party type in a new type whose only purpose is to express the suitability of a particular interface. And for no real gain at all (or at least I've never felt myself wanting nominal subtyping; perhaps it's a difference in workflow).

> judicious abuse of interface{}

Putting aside the contradiction of "judicious abuse", I don't think `interface{}` is very common in Go, and where it exists it's usually appropriate, for example, JSON de/serialization or Printf. `interface{}` is used very sparingly in Go, and considering that people build real, successful applications in languages like Python and JavaScript where everything is an `interface{}`, Go's use isn't that bad.

> Writing go can be difficult because of a lack of some language constructs that leave you unsure how to proceed.

I particularly disagree here as well. There's often one way to do something in Go, and I've had very good luck by Googling "How to do x in Go?".

> OTOH, things like criticisms about it's error handling are overblown. At best it's a mild nuisance, and more often than not, a good thing that makes error handling explicit and clear.

Agree here. Maybe I'm an unusually fast typer, but the majority of criticisms seem to be about how long it takes people to type out the `if err != nil {` boilerplate.

Personally my biggest criticism of Go is the lack of sum types (IMO I would much prefer sum types to generics). There are a lot of cases where it would be nice to express OR constraints. There are workarounds in Go--for example, using an interface with a private method that the variants must implement, but that's a lot of error-prone boilerplate and it tends to result in unnecessary allocations and dynamic dispatch (as opposed to a switch statement).

Another criticism is that the compiler doesn't optimize as aggressively as I would like, including that many abstractions are runtime abstractions that could be compile-time abstractions (specifically, Go doesn't have semantics for distinguishing between a run- and compile-time abstractions and the compiler pretty much always assumes that all abstractions are runtime abstractions.

> If you can, use goland. I use vscode and it's... not pretty. Almost every single thread I've seen on reddit recommends goland. My experience with vscode has been very unpleasant, particularly with go plus.

I've been pretty happy with vim. I haven't used goland. vscode became noticeably worse around the time that modules landed, and IMO never fully recovered. `gopls` has also been disappointing. Go's editor story used to be best in class in general, but it seems to have gone downhill. Still, I think it's well above average (especially with respect to the dynamically typed languages).


>if you have two juxtaposed slices backed by the same contiguous block of memory, growing the former will write over the latter, but you quickly learn that you should virtually never be appending to slices that don't have sole ownership over their backing memory.

You also may have to learn the hard way that saving a pointer to an entry of a slice that is later appended to may, or may not, depending on len()<cap(), produce an unintended copy.

>I particularly disagree here as well. There's often one way to do something in Go, and I've had very good luck by Googling "How to do x in Go?".

How about deciding whether a heavily-used pure-function method should take a whole struct as its receiver, or instead a pointer to it?

If a whole struct, then non-modification of the receiver is guaranteed by the compiler, a great boon to the code maintainer.

If a pointer, then any number of members may be added to the struct with no performance penalty per invocation.

How to choose?


> How about deciding whether a heavily-used pure-function method should take a whole struct as its receiver, or instead a pointer to it? If a whole struct, then non-modification of the receiver is guaranteed by the compiler, a great boon to the code maintainer. If a pointer, then any number of members may be added to the struct with no performance penalty per invocation. How to choose?

It seems like you answered your own question. I’m not really sure what you’re looking for here...

> You also may have to learn the hard way that saving a pointer to an entry of a slice that is later appended to may, or may not, depending on len()<cap(), produce an unintended copy.

That’s all pretty inductive from the simple definition of a slice: a growable view into a backing array (where “grow” implies copying to a new, larger backing array). But yes, it can be surprising if you are jumping into Go without ever reading the basics. In general, the best practice is to avoid multiple references into a mutable slice (this seems like generally good advice in any language).


I like Go and have no major complaints about it. I especially like the highly performant garbage collector and the fact that there are a lot of libraries for it. With the libraries you have to be careful, though, quality of 3rd-party libraries differs a lot on Github.

There is also one point of critique, I believe that Go got its concurrency primitives wrong. I fear people will hate me for saying this, but I say it because almost every library I've tested that uses lots of goroutines fails or produces some serious concurrency errors at one point or another. Not only are there many race conditions in Go code, there are also many libraries that silently fail. For example, scan Github for parallel directory walkers or check out uses of sync.atomic.

I personally believe that both transactional memory and the actor model are easier to use although it might be harder to implement and make them efficient. I also think that thread local storage and Goroutine identities would have been useful, despite what the language purists say. Generally speaking, I think Go's choices of concurrency primitives are too low level. (I'm not saying there is anything wrong with them per se, just too low-level for most of Go's users, and I'm not excluding myself in this assessment.)


Yeah, bounded channels ended up being more effective as a hype mechanism than a great concurrency primitive, I would say. Coming from Go and kicking the tires a bit on Rust, I was impressed w/ how much nicer it was to write parallel code using Rust + the crossbeam library than Go.

At the end of the day Go's concurrency story is just traditional threads and mutexes, with custom syntax for one particular datastructure.

But, you do get a great runtime race detector & the M:N scheduler, so you can use lots of goroutines w/o blowing out memory, avoiding async hassles.


I agree with a lot of this. I'm especially looking forward how generics should make it easier to build a library of high level concurrency patterns that people can use in Go, instead of everyone trying to build their own. Promises (which would allow you to start a number of parallel tasks, then wait on the results), "parallel for" with controlled amounts of concurrency, etc.

The M:N scheduler is great, and the ability to easily spin off Goroutines is really nice, but I think most people shouldn't need to manually launch goroutines most of the time. Goroutines should just be an implementation detail.


Always read the documentation of your libraries to see if they give any thread-safety assurances - if they don't mention thread-safety, assume they do not have it.

Concurrency in general, is really hard: hard to express and hard to debug - none of the languages I know do it perfectly. However, I like Go's concurrency primitives & tooling the most. Javascript and Erlang are runners up - for the record, the languages I'm familiar with are Erlang, Go, Python, Java, Javascript and C. I wouldn't know where C# or Rust would fit.


Just as it's not mentioned there, I've been running a Go newsletter for the past six years at https://golangweekly.com/ (yes, RSS is available) - the archive can act a sort of week by week "what happened" for anyone looking back to specific points in Go's history over that time.


Hey Peter! Thank you for doing this. I'm a regular reader and love what you do.


Thanks, very kind! :)


omg, I can't believe I forgot to add it!

Just added :)


Thanks, no problem! Congratulations on the success of your post!


Python is also said to be beginner friendly and easy to learn but in fact there are a lot tricks hidden somewhere and if you want to be a professional python programmer, there are lots to learn and they're very deep.

Go is indeed great for web-oriented applications, its built-in network stack is really handy, and you don't need run a nginx, another application server, etc, everything can be one single executable, it's like a docker itself.


Disclaimer: I don't know anything.

> Python is also said to be beginner friendly and easy to learn but in fact there are a lot tricks hidden somewhere and if you want to be a professional python programmer, there are lots to learn and they're very deep.

Yeah. I'd add that to become proficient, you are also going to have to learn a bit of C and know your way around Python's bytecode. You'll have to learn the language and the interpreter.

I've never used a compiled language (shame on me), but I'm not aware of people learning the Go compiler aside from a couple of its options maybe.

The static binary thing is amazing, I love it. By now, a large part of my selfhosted stuff is written in Go and all of it is great so far (no causality, "just" correlation).


I remember Rob Pike talking about how each language has its own way of doing things and it's bad to keep adding all features to all languages. This only makes them all end up being the same he said.

The Go way was code generation. After they included generics, I wonder if the go team has let go of that principle.


Code generation only solved part of the problem - saving the trouble of having to write repetitive code. It does not solve the maintainability problem because if something needs to change then you need to manually change all the generated code. I have been using Go since last ~6 years and I'm really excited that the only pain point I had with Go will soon go away \o/


Wasn't the ability to add a //go:generate comment enough to make it easy to re-generate anything that you needed?


Generation is a rather big solution for sometimes small problems. For many things like utility functions I think generics are much more readable and easier to understand while go generate would be heavy handed.


> it's bad to keep adding all features to all languages

I understand the reasoning behind this take - a lot of modern languages have wound up somewhat bloated, with features that are either non-orthogonal to other features and break the 'one way to do it' principle (Scala comes to mind), or with features that seem tacked-on and feel alien to the language (the latest versions of python come to mind). I understand, in theory, why go tries to buck this trend.

The problem is that in bucking this trend, go has (at least historically) not evolved enough to fix some of its own core issues. And it does have some big issues - nils, zero values, and non-thread safe primitives come to mind for me. I'm all for being thoughtful about introducing changes and making sure those changes are idiomatic and don't violate the 'ethos of go'. But avoiding making those changes in the name of 'keeping it simple' never made sense to me in cases where programmers are shooting themselves in the feet and already not having a simple time with your language.


Here’s a quote from Rob Pike (in 2015) about what Go was trying to avoid:

"Java, JavaScript (ECMAScript), Typescript, C#, C++, Hack (PHP), and more [...] actively borrow features from one another. They are converging into a single huge language."

https://www.dotconferences.com/2015/11/rob-pike-simplicity-i...


That's not entirely a bad thing.

We've learned a lot about language ergonomics in the past forty-nine years. Generics are super useful. Lambdas and streams can cut down on a lot of boilerplate. Immutables make code easier to reason about.

And I'm glad that I have access to all of those things in Java. I'm not upset that my workday language is being polluted by outside influences, because I'm not a purist, I'm a programmer. I don't care that records were lifted from Scala and Kotlin ... I care about how they can make my code better.

The idea that we should deprive ourselves of useful features because it makes languages too similar makes no sense to me.


You should watch the talk - I actually happened to do so yesterday so it's still in my mind.

The point is not that they're withholding features because it makes the language too similar to others, and I'm sure that's not what you were really thinking the argument is either. The thing they're trying to focus on is that with ever increasing amount of features, you have an ever increasing amount of complexity added, which has implications on the amount of conversations, decisions, readability and difficulty your team is going to have when tackling any given problem. It can really just be boiled down to increasing features past what is needed can be considered bloat, which does not come free to a developer or team's collective consciousness.

To quote/paraphrase from the 5:10 mark of his talk...

"If a language has too many features, or even more than you might need, you spend time programming thinking about which features to use... you might even spend half an hour messing with a few lines of code to [see how a given feature fits into the solution]... [and even more, when you revisit that code, you have to get back into the thought process you initially went through when determining which features to use]."


That's certainly fair. I was just talking to someone about how it used to be possible to hold the entire Java ecosystem in your head at once, but that was probably 15 years ago.

On the other hand, adding language complexity can decrease program complexity a great deal. Generics are the prime example of this. If you see List<Foo> and List<Bar>, you know exactly how both work. But if you see FooList and BarList, you might have to stop and worry about how each is individually implemented.

Or the whole "while is spelled for in Go" thing. Sure, they managed to strike out one whole keyword from their language, but anyone coming from another language is going to have a momentary brain skip while they try to figure out why that is, and how they need to re-write their while loop as a for-loop-but-not.


I was with you till the List<x>, but writing the while loop as a for loop is controversial. C'mon man. That is second nature for anyone programming for a few weeks.


This is a seductive argument, but unless you're making everything from scratch and working purely on greenfield projects, there is so much more complexity involved in learning the specifics of the local system and code you're working on than there is in learning the language(s) it's written in.

Heck, even if you're greenfield 100% of the time, the way the OS, the networking stack, the browser, any runtime support libraries you depend on, all work plus just figuring out how to model the problem you're trying to solve is still more complicated than the actual programming language.


Or you could see that languages have use cases, use cases overlap, languages should adopt useful tools for their use cases, therefore languages will often adopt the same tools as other languages with overlapping use cases.


It's 2021, the convergence still hasn't happened yet. It's never going to happen. I know Rob Pike is probably smarter than me, but this a bad hot take in 2015, and it's outright stinky in 2021.


Well look at patern matching for example, they're all adding that. Isn't that convergence?

https://www.php.net/manual/en/control-structures.match.php

https://www.python.org/dev/peps/pep-0622/

https://www.infoq.com/articles/java-pattern-matching/

https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/...

I could go on with various topics, immutability etc ... It's pretty clear that in the past 10 years language evolved more quickly and borrow concepts from other languages.


Languages evolving and borrowing good ideas from each other* is not the same thing as hyperbolically sneering at some of the most popular programming languages in the world because they're supposedly all converging into one single language, which they never were doing and never will do.

*Note that the new Python pattern matching has a somewhat controversial design, and it probably won't see significant adoption in the wild for several years, if ever.


> Languages evolving and borrowing good ideas from each other* is not the same thing as hyperbolically sneering at some of the most popular programming languages in the world because they're supposedly all converging into one single language, which they never were doing and never will do.

And yet PHP, Java and C# MVC web apps looks all the same in both syntax and structure. Same syntax for interfaces, classes, namespaces, entity models, query builders, controllers, views and so on. A modern Symfony application looks almost the same as a Spring one.


6 years is not a lot of time if you examine the progress of modern programming languages.

These languages are more similar to one another than they were 6 years ago. I don't know if it was meant to be taken literally, but I definitely don't think it's "outright stinky" to claim convergence.


They are all similar enough that a programmer fluent in one can start writing code in the style they are familiar with in another. Remember the old saying, "You can write FORTRAN in any language[1]"? Well, with the aforementioned set of languages, you can write one of them in another and it doesn't even look wrong.

[1] https://blog.codinghorror.com/you-can-write-fortran-in-any-l...


They are all similar enough that a programmer fluent in one can start writing code in the style they are familiar with in another ... with the aforementioned set of languages, you can write one of them in another and it doesn't even look wrong.

1. Only superficially, because they all share the same basic flow-control constructs and (except for Python) the same C-inspired syntax. They all have totally different standard libraries, package ecosystems, etc.

2. I don't see what makes Go exempt, other than "wow great they didn't add generics bless the Go core team."


Go was built as a language to make communicating sequential processes[1] first-class citizens. Thus the ubiquity of channels and goroutines. No locks, no condition variables, no callbacks[2]. Writing a bunch of code using locks and callbacks in Go goes against the grain of the language.

[1]https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf

[2]https://www.youtube.com/watch?v=f6kdp27TYZs


10 years later the language is still the same, not much has changed, I can't say the same about Rust, C#, Java, PHP, Python etc ... The only major thing that came in the last couple of years was Go module, and it's more on the tooling side than the language itself.


You can think of generics as providing support for a simple, common form of code generation in the language itself. That might not be technically precisely correct, but looking at it that way makes it seem like not a radical change at all.


Considering how long and how much discussion it took to implement generics (coming in 1.18 according to FAQ [1]), I'd be inclined to say it supports their principle.

I don't think they ever said they wouldn't implement Generics. It was a matter of maturing and consideration of different proposals.

[1] https://golang.org/doc/faq#generics


Yet Go's code generation is clunky even compared with Java's Annotation Processors.


Ken Thompson states that, initially, Go was purely an experimental project. Referring to himself along with the other original authors of Go, he states:

> When the three of us [Thompson, Rob Pike, and Robert Griesemer] got started, it was pure research. The three of us got together and decided that we hated C++. [laughter] ... [Returning to Go,] we started off with the idea that all three of us had to be talked into every feature in the language, so there was no extraneous garbage put into the language for any reason.


I was writing code in go for maybe a week before I noticed "make" was an overloaded function and subsequently noticing go doesn't allow you to overload your own functions.

Things as simple as this seem mundane, but it's painfully obvious discrepancies like this that make me avoid go when I can - if I'm going to utilize a new language, I'd prefer it to have interesting and consistent paradigms that lead developers to a similar understanding of how to use the language. Inconsistency in the core language like this doesn't give me a lot of hope for the longevity of go projects remaining easy to reason about.


To me, the fact that there are concepts such as generic containers (slices, maps) but that you aren't allowed to design your own is a concession that generics are useful, necessary, and not too complicated for the language. It confuses me that people then defend the decision to exclude other forms of generics. I was very interested in learning and applying the language at one point, but the inability to make very basic abstractions and the horrific proliferation of either manual or generated boilerplate code made me decide to shelve it permanently, or at least until go 2.0 comes out with generics.


I find Go the "perfect" programming language except for two things:

- the one to one mapping between directories and packages. It feels so inflexible compared to what other programming languages have (e.g., namespaces)

- that I have to hardcode "github.com" (or another website) in my source files. Sure I can do "find and replace" if needed, but this kind of stuff shouldn't really be within the `.go` source files. Just add a `dependencies.txt` that maps names (used in code) to urls. Easy

Having said that, I enjoy writing Go code.


I really like go. Its simple in that you can learn the language pretty quick. I might get burned in another comment below but it's the language that feels the closest to C to me. Both are really simple languages that can get relatively complicated pretty fast. Inheritance through embedding for one feels a lot like using -fms-extensions in C.

However I think the coolest part of the language is the part no one talks about. The inclusion of comments as a major part of a program.

// +linux

for managing files for specific platforms is just wonderful.

//go:embed

for embedding files and directories into your program

// #include <stdlib.h> import "C"

is probably the coolest and easiest ways to use C libraries I've seen. You can write C code directly into your go files and even set complier and linker flags.

//go:generate

can allow you to automate your build process from within your codebase.


Go: we have so few keywords! Simple language!

Also Go: because we shoved them all into comments instead!

This seems like an extremely weird choice.


Yes, I agree. Also (with the big caveat that I am not even a little bit of a language designer / implementer / lawyer, just a guy interested in these PL topics from early in my career), the use of comments, which are meant to be ignored by the compiler, to make certain actions happen, seems non-orthogonal and not a good choice. Like shoehorning something into something else it was not meant to be or support.


As someone who uses the language every day, I fully agree. My work would be easier if they all conformed to some syntax not easily mistaken for an ordinary line comment. A standard preamble of '///', say.

No doubt sophisticated IDEs highlight them somehow, but more naive editor extensions e.g. Emacs 'go-mode', do not. No color-coding.


As someone who uses the language. I think it's one of its most overlooked features. By convention comments are ignored, but the truth is they're not by the programmer. Comments are extremely important to keep track of what you're doing and reading other people's code. So I think this is a case where the convention doesn't meet the reality. Also using these features within comments makes it so every use of these extra features is documented and shows up in completions, so you the programmer always know what you're doing. I'm a fan haha


>By convention comments are ignored, but the truth is they're not by the programmer.

Disagree. It's more like the opposite is mostly true:

By convention comments are not meant to be ignored, but the truth is they mostly are, by the average programmer. And most programmers are average, by definition of the word 'average' and by the bell curve law. Yes, with exceptions.

Even if comments are not ignored in normal work, just wait for the next crunch to hit (which is mostly the default state or mode in many companies, anyway), and then see the fun. Every dev scrambling to hack the code any which way to meet the deadline / target / ship date, and now maintaining comments is a goal that flies out of the window. Been there, seen that, plenty, though also worked in places where that was not the case. Those are few in number, though.

There can be other issues too, with compiler directives as comments. What if the directive-as-comment is itself now commented out, maybe as part of commenting out a larger surrounding block of code?

Now you (and the compiler writer) have two problems :)

http://regex.info/blog/2006-09-15/247


>>By convention comments are ignored, but the truth is they're not by the programmer.

>Disagree. It's more like the opposite is mostly true:

>By convention comments are not meant to be ignored

Otherwise who on earth would write them? Who would like to write stuff that "by convention", is not going to be read?


Documentation links (incomplete):

  // +linux
Dunno -- help, anyone?

  //go:embed
https://golang.org/pkg/embed/

  // #include <stdlib.h> import "C"
https://golang.org/cmd/cgo/

  // go:generate
https://golang.org/cmd/go/#hdr-Generate_Go_files_by_processi...


> Dunno -- help, anyone?

https://golang.org/cmd/go/#hdr-Build_constraints

EDIT: Those are in the process of being replaced with //go:build directives. See https://github.com/golang/go/issues/41184.


> // #include <stdlib.h> import "C"

> is probably the coolest and easiest ways to use C libraries I've seen.

You should see Zig's interoperability.


This is a great article, but it felt needlessly defensive on the "lack of assertions" thing. Admitting that something is missing makes the rest of the article feel more believable. No need to say stuff like "maybe the lack of asserts makes the test code build faster so it's actually a good thing." Just say "unfortunately, no asserts."


I've been using Go for a personal project of mine recently. I had briefly used it at the tail end of my internship while getting my degree, but nothing particularly in-depth, and it was some pretty simple server code that never really challenged me.

There's definitely some nice stuff about it, but there's also some stuff that's a bit of a pain. For one thing, being straight up unable to compile a program if there's an unused variable, not even a debug compiler flag or anything to allow it while testing. There's a lot of cases where I just want to move one thing around, or I'm partway through writing a function and just want to see what state something is in at a point, so I want to throw a breakpoint on a line and run the program up to that point. But even though my code is technically all valid, because the variable I want to look at is unused, or I commented out a line that makes something else unused, I now can't compile at all. So there's a lot of times I'll have to throw in a random fmt.Print of a random property of index zero of an array or something, which might crash anyway if it's reached, but at least it'll compile. It maybe doesn't seem like it would be that big of a deal, but when you've got a train of thought going, and you're trying to iterate fast, it can really pull you out of the flow of things.

There's also a lot of weird situations where I find it hard to keep things clean. For instance, I like using this shorthand:

  if err := doThing(bar); err != nil {
    // handle error  
  }
But if you're doing it in a case where your function returns a result and an error, i.e.

  if foo, err := doThing(bar); err != nil {
    // handle error  
  }
That's indeed valid code, but foo only exists in the scope of that error handling. So you'd have to declare it above with its type

  var foo FooType
  if foo, err := doThing(bar); err != nil {
    // handle error  
  }
But you can't actually do this because when using type inference, both return values have to not be declared beforehand. So you have to pull err out into a declared var with a type, and remove the type inference altogether like this

  var foo FooType
  var err Error
  if foo, err = doThing(bar); err != nil {
    // handle error  
  }
And from that point on, if you're using this pattern again anywhere else below in that function and you want to continue using that slick error handling shorthand, there's barely any point because err already exists in your scope, and you'd just be redeclaring it.

Perhaps there's some tricks I don't know about, but I tend to run into situations like this a lot, and I spend so much time trying to appease weird edge-cases of the compiler's strict rules that is really throws a wrench in my workflow.


Just do

    foo, err := doThing(bar)
    if err != nil {
        // ...
    }
and carry on with your life. Go is not a language in which you obsess over small things like these.


I was thinking that as I was reading. Its 1 extra new line, and arguably more readable.


So Go is a language that values the compiler writer over the programmer?


The difference between those two blocks is that a semi-colon was replaced with a new line.

_How insulting_.


It's okay to be annoyed by patterns you don't like!


It's very opiniated language.

And to be fair, I prefer to do this in 2 lines, since it's simpler to read (less going on in each line.)


The corporation over the programmer.


I always fall back on this pattern when the return values need to be used further down:

  foo, err := doThing(bar)
  if err != nil {
    // handle error
  }
  foo2, err := doThing(bar)
  if err != nil {
    // handle error
  }
Only one value in a multi-valued assignment has to be undeclared to use the short variable declaration operator (:=). If the variable already exists in the current scope and the type matches, it will get reused.

https://play.golang.org/p/qo141lT-Jjp


>If the variable already exists in the current scope and the type matches, it will get reused.

That's good to know. I suppose that would probably be the cleanest way of doing it. Thank you!


A workaround: assign the unused variable to underscore.

  _ = myUnusedVar
Now you have used it.


That's smart, I never thought of that. Thanks!


I have a modified go compiler which ignores the unused variable rule. If you are interested, I can share it with you.


Well, you just pointed out why the shorthand is only stylistically correct for functions that only have an error return. Trying to force a convention into a completely different situation is going to cause pain in any language.


To "use" unused variables, I pass them to the following function:

    // PretendToUse is an empty function to shut up the Go compiler while prototyping
    func PretendToUse(args ...interface{}) {}

   PretendToUs(var1, var2)


I've been a professional Go developer since 2015 and I prefer to never use the shorthand. Saving a single line is never worth how much harder it is to read.


I find the shorthand clearer. Having to ignore less boilerplate code often makes it easier to process the highlights of what is happening.

(I know people are awfully smug about paying more attention to the "unhappy path," but when it comes to reading and understanding the intent behind code, understanding the happy path comes first).


you could use an `else` clause for the scope:

  if foo, err := doThing(bar); err != nil {
    // handle error  
  } else {
    // handle foo
  }
but most don't use that form and just do the call outside:

  foo, err := doThing(bar)
  if err != nil {
    // handle error  
  }


or pass doThing as a function parameter

https://play.golang.org/p/4PoK6EZc4W9


Is this updated? I see in the suggested resources to learn Go there is a book created in 2015... The language and related patterns changed so little that that book is still good?

Or anyway, do you have any to suggest?


> The language and related pattern changed so little that that book is still good?

Pretty much yes, and TGPL/Effective Go are still very good resources to learn the language, if not still the best available.

Probably the biggest changes since 2015 were constant stdlib improvements / extensions, and things like the context API becoming standardized across the ecosystem. Oh, and Go modules. Otherwise the language is pretty much unchanged.


I hope they write a second edition, and give the cover a red "GO 2" stamp graphic, like they did with ANSI C on the K&R second edition cover.


Maybe '<decade/year> edition' would be better :)


If you mean The Go Programming Language then yes, it's still a fine book. It doesn't cover new additions like Go modules, of course, but in terms of the core elements of the language, it stands up well. Are you going to probably want more once you've covered the essentials? Yes, but it'll put you in a great position to take on further material.


I still use my K&R which proudly proclaims that it includes the new ANSI C 89.


Nice article...

Over here I've invested a lot in Java, to be used where static typing is necessary, JavaScript (Node.Js), Elixir and PHP on other occasions. But it's beginning to look like Go will displace Java. So, might be high time we learned Go :/


It's only in hyped up circles where you hear that golang will displace Java. The latter is not going anywhere for the foreseeable future, and it keeps improving, whereas golang is stuck in the 70's.


The only production Go code I ever see is in a kubernetes operator. The backend services I see deployed are probably 75% JVM, 25% Node, and less than 1% something else.

Go is popular at Google, but we're kind of past the point where something being popular at Google makes it popular everywhere.


(googler, opinions are my own)

Google still has LOTS of C++ and Java. Like most companies, we're not going to rewrite large code bases in other languages just because its "new" (comparatively). Greenfield projects can move to new languages more easily.


Twitch.tv uses mostly Go AFAIK. Where I work we use Go in all our services. There's plenty of uses of Go. You just won't ever hear about them, because why would you? You see Java and Node because that's all you've ever worked with. I see Erlang, .NET and Go, because that's all I've ever worked with.


Work at a large telecom co., who used to be Java only, I only write Go. Hn threads like this is when you realize it's core user base.


Just look at the recent Java 16 hn thread. Java is not going anywhere in the next 2 decades.


Umm ... what exactly makes you say that?


Very soon whoishiring will start a thread for April. I bet you are going to see more Go opportunities than Java.


Is it me or did Go fail its original mission to replace systems languages (c/c++), but then somehow achieved victory by loads of Python devs embracing it as a far more performant runtime for their code?

Whichever way it is, as a lisper I just find the language tedious and ceremonial, and its csp implementation lacking.


I feel like Go is more similar to another Java or C# in the role that it fills in enterprises.


Does Go have tail recursion? I'm guessing not since Go isn't big on the "optimization" part.


Tail recursion isn't a computationally/memory intensive optimization to make, so it's been in since the beginning.

It doesn't guarantee that it will make that optimization though, unlike some more functional languages that rely on that to generate the same stack consumption as a for loop would.


> Go isn't big on the "optimization" part.

Care to elaborate?


It's true. Go prioritizes compile speed over a ton of optimization. It does fast ones, but it doesn't do the expensive ones.

I don't know whether the Go compiler never does tail-call optimization, but I do know it is never something you can count on.

(There's two kinds of tail call optimization... there's the one where the compiler may choose to do it as an optimization step, but you can't ever count on it because at any time for any reason it may stop, so you have to assume it didn't happen. Then there's the kind you can absolutely count on, and program tail-call-based algorithms secure in the knowledge that they won't blow out the stack. I'm pretty sure Go doesn't have the latter.)


Thank you for clarifying.

Does this apply to procedural code as well? Go not implementing TCO makes sense as the language is _very_ Wirthian.



FWIW, I read "tail calls" as referring to the optimization that allows you to rely on the tail-recursive calls not generating stack frames, on the grounds that I'm not aware of any language that specifically forbids tail recursion.


Depends on what you're optimizing for.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: