Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why I Don't Like Golang (2016) (teamten.com)
140 points by bravogamma on June 14, 2022 | hide | past | favorite | 222 comments


They've fixed the import / modules situation to a point where it's usable and much improved, and generics have been added.

However, the issue this brings up about structs / types not explicitly declaring which interfaces they implement is a real and unaddressed problem, especially in large codebases. The only tool that I'm aware of that finds implementations is GoLand, at steep JetBrains prices.

Figuring out what type an API is asking for should not require reading every line of code in the package, and slows down every developer of large Go projects


When developers who usually make 6 figures of money complain about $90/yr IDE price I just cannot help laugh. Actually it's even better... the price elevators down from $90 -> year2 90 - 20% -> year3 90 - 40%.

Over three years that's like $150-$200 total and it will save you so many headaches. But that's a steep price? Are you kidding? Why do developers hate tools that cost money when they save them time and allow them to do more?


Sorry, no. I have JetBrains and have since stopped using it for go development. It's too slow, too buggy, and lacks the ecosystem that VS code has. I still use it for Java because the rest are even worse

A language that depends too much on IDE integration for usability is a real problem, because now you have tool fragmentation as everyone goes different routes with varying levels of success to fix the deficiencies in your language. In the end you end up rolling your own tools as I have done, which is the absolute WORST of all worlds.

Go was supposed to be simple, but all it succeeded in doing is shifting the complexity elsewhere and calling mission accomplished. When you're designing a language, it's VERY important to understand the difference between the emergent complexity of the domain, and the inherent complexity of your design. The latter can be fixed, the former can only be managed - in ways that are already well researched (or just swept under the rug, as go has done).

Too much magic and too much "clever" re-purposing of existing paradigms (file names, capitalization, implicit contracts, etc) makes for an infuriatingly bad design.


While I am getting your points, but IDE support is an incentive to use a language in my eyes.


I'm not complaining about IDE support. IDE support is great!

IDE dependence is not.


Pretty simple for me:

I like working with Free and Open software much more than proprietary software. I think it's important for society, and I have more fun that way too!

Also the payoff for me has been very good, I can learn emacs once and enjoy using it for the rest of my life for all significant written language tasks on a computer.

Perhaps I could be a little more efficient if I were using a jetbrains IDE, but then I wouldn't like what I was doing as much. Enjoying what I do, even if it may look slightly contrived to others, is important in me achieving results at work.


Most of JetBrains platform is open source (Apache license). Only the language plugin is proprietary.

It's not perfect but better than your typical closed source software.


This argument is not convincing to me, especially considering JetBrains publishes their IDE base as open source.

Everytime I have to use VSC to develop typescript and angular, I am having problems with finding definitions (works 30% of the time), code search (it takes longer due to the constricted interface), git operations (want to do more than a simple pull and push? good luck), and much more. WebStorm on the other hand has a lot less bugs, a more flexible interface, and more features. I am glad that people make an effort to make an IDE instead of an editor with IDE-style features and I'll gladly pay a very small amount of my salary to them.

Every workshop has higher costs than a software developer. Imagine a car mechanic propping up a car with 2 by 4 because they use what's available for free. No, they buy their $30,000 lift because they need it to get their work done quicker.


> Every workshop has higher costs than a software developer. Imagine a car mechanic propping up a car with 2 by 4 because they use what's available for free. No, they buy their $30,000 lift because they need it to get their work done quicker.

This is a far from convincing argument. Jetbrains IDEs are not the equivalent of a professional lift and the competing (often free) products are not the equivalent of a 2x4.

Is Jetbrains good? Well, I've used it for Java and was pretty impressed.

Is it $199/year[1] better than the free stuff? Well many people don't think so. It's fine if you only every use a single stack, but most of us use multiple languages and multiple stacks, now you're looking at $649/year (see link below) for all tools. Considering that my current personal development computer cost less than that years ago, is it now wonder that the price is considered too much?

I think the problem is that developers are looking at the Jetbrains products and comparing it to the value they get from other development purchases.

Compare:

A single $1k computer will last for many years, do every single development task needed to make money, be used for entertainment, and write all the actual software that will be sold. When it is too slow for dev (in a decade from now), it'll be repurposed for something else.

A single annual payment of $649 to JB results in a tiny increase in dev speed, which will disappear at the end of the year anyway. It won't make the code more robust, it won't help solve business problems any faster, it will only make code navigation faster.

For a dev, look what $1000 buys, and then look at JB for $650, and it doesn't look like all that good value for money anymore.

[1] the cost for Goland at https://www.jetbrains.com/go/buy/#commercial


Just wanted to note that if you're talking about "personal development computer" and "developers loooking ... and comparing", then it's more like an "individual" license as opposed to the commercial one, which is meant to be evaluated by companies.

The individual-license pack for all of their IDEs would set you back $250 as opposed to $650 commercial license.

I used to use "IDEA Ultimate", which is 30% cheaper than the All-Pack and supports installation of most of the other language plugins, allowing me to use a single IDE for everything. Nowadays I'm using separate IDEs as that seems to work faster.

I personally find the price worth it, as even a simple "expand selection scope" operation which I use many times per day just doesn't feel right in VS Code.


JetBrains' All-Products Pack costs me only 150€/year + VAT (250€ individual license minus 40% for subscribing for 3+ years) which is also fully tax deductible. In the end it costs me less than my cinema budget for a year of top notch products with proper support and bug fixing where I don't have to wait months for an answer.

I stand by my 2 by 4 comment because they get the work done, just slower and more awkward. It's always super amusing how software developers with one of the highest salaries around the world (yes, even in poorer regions) complain about costs when other jobs require tens of thousands in initial investment.


As others have pointed out, you probably meant to link the all tools pack for personal use to make a comparison against: https://www.jetbrains.com/go/buy/#personal?billing=yearly

Currently that shows a cost of 250 euros per year, or about 20 euros per month (excluding VAT which varies).

For most developers, that is indeed a relatively small amount (even I opted for the ultimate package, despite earning in the low 2 figures in Latvia), whereas the 650 euros for commercial licenses would be doable for any organization that cares about their developers' experience.

All of that is excluding their loyalty discounts, programs for students and non-profits, startups etc.: https://www.jetbrains.com/go/buy/#discounts?billing=yearly

Personally, whenever I see commercial software or a SaaS/PaaS/IaaS solution, I'm tempted to throw a brick through someone's window (figuratively) because those are likely to result in unreasonable amounts of vendor lock (especially with cloud services around Kubernetes management), but personally I haven't found a better IDE than what JetBrains offer.

For Java, all of the alternatives are worse: Eclipse is buggy and crashes (though some swear by its incremental compiler and integrations), NetBeans is kind of dated and struggles with projects that have 4000+ source files (though it's cool that Apache keeps it alive and there's the whole module enable/disable functionality and their VisualVM integration is great).

For .NET, Rider is easily up there with Visual Studio, even when you're doing something more niche, like working with the Unity game engine (the performance hints are nice), or just working on .NET apps.

For PHP, Ruby, Go, Python and other languages their tools feel competent and oftentimes suggest you whatever it is that you might want to do, be it setting up your runtimes properly, your dependency management systems, install all of the dependencies, import the project config/launch profiles etc.

For Node/JavaScript I have never found a good IDE, but maybe that's because the language is sometimes a mess to work with - e.g. getting only some very basic completion in some garbage 3000 line AngularJS controller because even the IDE has no idea what the hell is going on there, or having Vue 3 use the <script> tag for adding code imports, instead of detecting that i'd like to use <script setup> but then again, they're pretty speedy with updates and if you don't do anything too crazy with projects, then it should be good.

I don't have much experience with their C/C++ offerings, or their lightweight text editor (Fleet) or the likes of DataSpell, though their DB management offering, DataGrip is pretty okay too! Though you can also configure the individual IDEs like IntelliJ to show up hints for most decent frameworks.


Sure I also like to use emacs since it stood the test of the time but the point was not proprietary is better but unwillingness to support companies that sell products.

If we refuse to buy products then we end up with companies offering just services with vendor lock in. Well...we already sort of ended up in such world.


I would be more supportive of this message if it was a $90 LSP server that you could support by buying (a la intelephense), but software developers are very opinionated about their editors and not everyone wants to use IntelliJ products.


Professional developers in the USA usually make 6 figures.


Musicians and carpenters make far less than programmers, but they still buy their tools. Software people are too entitled. Even if you are not professional, you can still afford 54c a day ($200 a year). Even a programmer in India can.


Many junior developers in my city make around $300 a month. After spending $200 on rent and most of the rest on food, you're not left with much. I understand nobody really cares about crap-holes like mine when they're making most of their money in Western Europe and North America, so just throwing it out there.


As highschool student in 80's Portugal, 10 years after 40 years dictatorship and colonial war were behind us, when software was sold as bootleg copies on bazaars, I saved enough money to eventually buy Turbo Pascal for students at 30 000 escudos.

As I wanted the real deal and not a bunch of no name floppies with copied manuals.

That would be around 150 euros, without taking into account the inflation to modern days.

And to place the price in perspective, it was a third of the minimum wage, while the overall cost of my PC took 5 years for my parents to pay back to the bank.


Fair enough, but what's the revenue per dev in your org then? Surely it's a bit more than $300/months.


I'm not paying that much for an editor that runs counter to my muscle memory, just so I can use a different programming language.

If a language requires some IDE to make it usable, then I put it in the same camp as Java: Hope the competition are using it.


No wonder FOSS languages are stuck in the pre-historic tooling.


If “pre-historic” means “doesn't take several minutes to start and require 8GB of RAM”, I guess that's a good thing.


I wonder how Turbo Pascal IDE managed to fit into 640 KB....


Because it wasn't based on bloatware liks Electron. And I guess the developers actually cared about performance because at that time they couldn't just assume that everyone has a powerful machine.


Electron !== IDE.


"IDE as tool for everything" is what's prehistoric, at this point.


Sure, some people enjoy being stuck with workflows born out of phosphor terminals.


So why don't you program in VR then? Why not generate CI jobs from an ML model?


Graphical IDEs are good enough.

Xerox PARC already showed how.


Do you have any other workflow that allows so much programmability and composability while being lightweight and cross-platform?


You mean the "cross platform" as long it is UNIX?


Enjoy duplicating your workflows anyway for CI. Or do you make a release by clicking the play button in your IDE?


Someone apparently isn't aware that IDEs can consume build scripts as project definitions.

I don't duplicate anything, my CI/CD pipelines consume MSBuild, Ant, Gradle, Maven, CMake, XCode, package.json, gulp, webpack files just as easy as my IDEs.


Have fun clicking around until it works


Why would I need to do that?


Look, either you're using "workflows from phosphor-era terminals", or you're clicking around in your IDE.


Windows has WSL. Is there any other major non-Unix platform?


Game consoles, with their Windows based IDEs.

Mainframes and embedded OSes, the latter with much more market share than all desktops together.


Are you programming on a game console???


> Game consoles, with their Windows based IDEs

Unfortunately English comprehension skills is a lost art.


That's why I buy JetBrains all products pack. It supports many languages.


I'm tempted to ask what languages you're using and what has your best experience with them been like.

Because as messy as Java is, refactoring codebases in it that have been kept alive for close to a decade is surprisingly not madness-inducing (most of the time), at least in some of the sane frameworks. Apart from, you know, legacy projects basically killing your career in the long term.

I'm not sure what other language I'd feel comfortable with changing how some method works across 50 other places that call it and have the IDE do most of the heavy lifting.

Yes, I have Stockholm syndrome, probably. Yes, I'd prefer to retire to planting potatoes in a farm, rather than work with NullPointerExceptions.


How much would a carpenter pay for a fancy bench top for their workshop? I'm guessing not a lot, since they can make one themselves.

Programmers are toolmakers, and are therefore harsh critics of tools they use; just like a carpenter will tell you everything that's wrong with the design, and choice of wood that went into a pricey, but ultimately-affordable-to-a-carpenter bench top. Having access to cheaper, good-enough alternatives is part of it.


A carpenter is not going to make his/her own table saw. A basic table saw costs $300ish.

I'm not writing my own IDE or my own database. I can, and I have, in times past. I do pay for tools that I need.


> A carpenter is not going to make his/her own table saw.

This is where the analogy breaks down, but they'd likely download a free one made by a consortium of other carpenters, which can be customized to their needs

> I do pay for tools that I need.

As have I: I was paying JetBrains yearly until they published plans to brick my IDE if I dared stopped sending them money. They walked this back after an uproar - but that episode showed me that I was also playing in their sandbox and subject to their every whim. I now default to using tools that can be forked at a moments notice (by myself or others)

Also, JetBrains IDEs were far ahead of the competition back then. For the tech stack and codebases I now work on (or perhaps additional experience?), none of the JetBrains IDEs are worth the effort. vim and a handful of plugins & scripts are adequate 95% of the times, VSCode takes me up to 98%, and it's diminishing returns beyond that


This is an undue attack. We buy our computers and screens and keyboards, most of us are on commercial OS, we pay for SaaS at a non trivial price, and most of us also use additional personal apps that aren't free (including password managers, mail clients etc.). Those are actual tools for the trade.

Arguing we should also buy every Jetbrains product is like arguing carpenters should buy line drawing AR goggles. Perhaps some will see the value in it, but it's far from an noncontroversial POV. If you feel a tool as as much downsides than upsides in your workflow, you don't use it, whatever its price is.


“I’ll pay $1000’s to get all setup and running with everything I need, including a bunch of stuff I don’t need, but not the last $90 for some productive software to make more money”

Yeah ok.


Some people really like these tools, and that is OK. But not everyone has the same workflow. I personally get a JetBrains license from the project I work on - but I refuse to use it, as I find their tools pedantic, slow, and eating my computer's resources. Ymmv.


Oh absolutely, if someone perfers emacs or vim, VS, VSCode, some JetBrains IDE. Punch cards.

Whatever makes them happy and productive, that's great. I've seen people use Vim and its crazy how productive some people are with it.

My point is complaining about the price of some software like it's blocking them from doing anything. They spend all this money on all this hardware and software, but when it comes to development, oh it costs too much I don't want to pay $90 for something to earn money...


The initial [0] only mentioned the steep price in passing, and primarily lamented the inefficiency of the design. I'm not even sure from that phrasing that implying isn't a paying user, even if they seem to care about people who wouldn't fork the money for the editor.

[0] https://news.ycombinator.com/item?id=31734755


Is this a JetBrains commercial account ? Am I at fault for not liking their product that much, and thus not paying them their apparently deserved yearly subscription ?

This feels pretty weird to be faulted for using other companies' products. And no, switching to Jetbrains' doesn't make me more money. Could be the reverse from my past trials.


It's not about liking or not liking a product. You're complaining about pay for a product. If you like a product and its helpful why are you against paying for it?


> If you feel a tool as as much downsides than upsides in your workflow, you don't use it, whatever its price is.

↑ that was in my answer (with the typo, on the “as as” instead of “has as”, my bad)

You might be confounding mine with another comment. I recognize the talent and expertise of JetBrain’s staff, but don’t like their products in general, and use VSCode as a primary editor, and (paid) Textmate for the rest.

To your general point, looking at project like Bitwarden, with their initial kickstarter and their current revenue, I don’t feel like people are restraining from paying for useful software, even when it has a generous free tier.


You forgot the additional $1000 for a computer powerful enough to run JetBrains.


Sorry I meant to type $1000’s. Corrected it.

I ran JetBrains software on one of those 1.5ghz MacBook 12” and it was totally fine.

I don’t think it needs “powerful” hardware.

Not like visual studio. Now that’s a pain!


> I don’t think it needs “powerful” hardware.

That depends. If you have multiple large projects open at the same time, it'll eat a lot of RAM, even if it won't be too CPU intensive.

I run it on a ThinkPad that has 32 GB of RAM, when I have about 6-7 instances of the IDE open and all of these services running locally (generally Java projects, the largest of which is around 4000+ source files), then it gets close to the resource limits.


My ThinkPad with 32GB of ram with 5+ instances of Rider, and DataGrip, VSCode, all open, switching between instances is quick. (checks number of source files for top 3 projects I work on daily) with 4684 files, 10230 files, 7211 files. (no npm junk)

Switching between Visual Studio is 15-20 seconds of wait time while it decides if any files have changed.


If you work for a company, in any field, and they don't supply the tools for you to do your job, they are doing them selves a disservice.

If you want your employees to be productive, you provide them tools to achieve that, otherwise you get what you pay for.

It's the Companies that are acting entitled in this situation, not the workers.


1. A Musician has one tool. Ok, maybe a note-stand etc. But in software, there are thousands of "tools" you can buy.

2. Physical tools vs making copies of some bytes. No need to retread this here, but bottom line: Not comparable.


No semi-successful professional musician is so bare. Software engineers are not your average starving type of people (unless they are willingly doing intermittent fasting!)


I wonder if anybody has 'one' guitar. I don't really know anyone who does.


We pay $3500 every 2 years to have up-to-date hardware


And if we try to re-sell our equipment to try to recoup some of that cost, best you can get is like 1/4th the original price typically by the time it's time to sell


Putting fundamental features of the language behind a price barrier at all will keep students and people who want to experiment with the language out of the ecosystem. This isn't a papercut, it's an intentional omission that has backfired and can only be fixed by tooling. If the only way to access an often necessary feature is proprietary, why not make the whole language proprietary at that point?



The language has a tool that does this, and every editor I can think of uses it (https://github.com/golang/tools/tree/master/cmd/guru), specifically implements.go if you're interested.


Guru has been replaced by gopls, using the common LSP protocol.


What's even worse is getting companies to pay for this stuff. Getting a company to buy software to help you do your job is like pulling teeth.


any company will let you expense a few hundred dollars for a tool you use literally every day many hours a day. if you’re having trouble they are not good to work for or you’re asking the wrong way


> if you’re having trouble they are not good to work for

That is true, but also in some places that is also the status quo: especially in countries where the developers don't get 6 figure salaries and don't create as much value to their respective companies.


how is this not common sense


I concur.


if a company wont pay for an IDE for there developers then I certainly won't be paying for it out of my pocket.

if they can't justify paying ~$100 for my ide of choice, then they are costing them selves far more in lost productivity, and that's not my problem.


IDEA is 650€/ year for our company.

Not saying Company should not pay that, but it's not $100


I dunno I've been in plenty of situations where a department head will literally beg me to buy things so they can reach the same budget spend as last year so their budget isn't slashed (DoD), and have seen the same situation in VC-backed startups looking to come up with tax write offs. Businesses for the most part love spending money and would rather do that than pay taxes, which is definitely a problem with corporate society these days.


No sane business wants to spend $100 to save $15-30 in taxes.


Tell that to Amazon or any FANG company that doesn't pay taxes because their clever use of writeoffs and "losses"


They're spending $100 to invest in the growth of their company, which is quite different from "love spending money" or "begging for something to buy" to reduce their taxes.


In most cases you cannot use personal licence on a company hardware. JetBrains also have separation between individual licences and organization licences.


JetBrains allows users to use personal license for work, but declines company to buy cheap personal license. I think this is good pricing. I agree that make company to buy license is annoying.


I'm not talking about the distinction between free non-commercial and commercial licenses. I'm just talking in general that it's tough to get companies to purchase any software.


I'm shocked at how often I see software engineers not paying for Sublime Text. These are people who get paid to write software not paying for software.


The use to pirate stuff all the time. Now it's actually more convenient for me to buy it.

But the philosophy I always had was that as long as there were enough people like you paying for software, I wouldn't have to.


I think most people in the industry pirated software when they were students or just beginning. But now that we make money it’s easier to just buy it.

Friend of mine pirated photoshop when he was studying and after he finished and started his own business he started paying for it.


Photoshop being easy to pirate was a feature for Apple: the uptake was absolutely insane across all possible levels and eventually nobody uses anything else. Same with Microsoft in 90s: when students asked Bill what he's planning to do with the whole China copying it, he replied that let them get used to it and we'll find a way to charge later.


*a feature for Adobe, not Apple


All too often the proprietary piece of software sucks, including their price, but its still the best. For example Burp Suite.


I've looked at it before and I just don't get the appeal. UI doesn't feel sexy enough for me to spend 80% of my day in it.


I think a tool like GitHub Copilot is worth far more than $90/year and I'll gladly pay for it. Probably up to $500/year or even $1000/year I'd pay for Copilot. It has saved me so much headache and time. There's no real competitor for that product and I'm looking forward to it going GA. I'd use emacs/vim and Copilot any day over an IDE.

But I'm not sure any IDE is worth $90/year when VS Code is free. The extensions for VS Code are next-level, especially the SSH extension. No other IDE comes remotely close to how well that extension works for its use case.


With VS Code, you pay with your privacy and your resources. These add up slowly and silently.


Well, the companies won't buy it for them without wasting about 10x the cost in approval paperwork.


Not to mention how much the company paying their salary (who should be paying for their IDE) makes!


Regardless of the price jetbrains intellij based editors are so slow they are borderline unusable.


> However, the issue this brings up about structs / types not explicitly declaring which interfaces they implement is a real and unaddressed problem

You can do this with a line of code below the struct definition, something like:

  var _ <interface> = &<struct>{}
The compiler will also generate helpful errors if the struct doesn't implement the interface.

Not requiring struct definition is a great feature in golang. I'm able to add an interface to a struct defined in another library to inject a different implementation for unit tests.


It would be cool though to be able to optionally declare the interfaces implemented by your struct, for documentation and tooling purpose. Also to validate that the struct you’re writing actually implements the interface you have in mind. I know you can make an anonymous assignment to check that, but some sugar on that would be nice.


> is a real and unaddressed problem

No, it's not. It's one of the best features, and I wish every language did this. At least TypeScript does it, too.


It's more than that though. There is just no standard library to be spoken of. I mean things like the most basic string processing methods, etc., are left to the programmer to sort out themself. Every time I've done contracting work on Go projects, I'm shocked by the things I have to implement from scratch (not that it isn't fun) that we take for granted in every other modern compiled languge I've tried (Rust, Crystal, Nim). I know this is more of an environment thing than a language thing, but in practice, language and environment are so intertwined it's pointless to not consider them together.


This isn’t true. Go has a basic standard package list. https://pkg.go.dev/std


Another big area that is lacking is Go makes it so hard if you want to use something other than a primitive as a hash key (Rust is guilty of this as well, mind you). This is something that should come out of the box in any modern language in my opinion.


> Rust is guilty of this as well, mind you

#[derive(Hash)] struct X{ ... }

Seems easy enough to me? The only annoyance is if a third party type didn't implement Hash, but you can solve that with a manual implementation instead of a derive.


> you can solve that with a manual implementation instead of a derive.

How? I thought the orphan rule said you can only define trait implantations at struct definition or trait definition


Yeah, for foreign types you will have to use the "newtype" pattern, aka: define your own wrapper struct.


ding ding ding! This has been a problem for me a number of times. The quick workaround is pray that it implements Debug and in a reasonable way and take the hash of the string instead


Maybe I misunderstood you, but you can use structs as keys of a map.


not if it doesn't have equailty defined. Then you have to define it. Oh, and it's an AST node someone at X company wrote and there are 100+ node types. Have fun!

In crystal there is a reasonable data-based default hash implementation that you can just rely on.


The map key type can be anything for which `==` is defined. This includes structs for which `==` is defined for every field. Composite keys etc are trivial.

https://go.dev/ref/spec#Map_types


Yes, and it's missing tons of things we take for granted in other langauges. If you look at the "strings" section it's so short. There aren't even convenience things like string reverse.


Odd, I find the standard library very full-fledged and useful, including the strings package. I've almost never needed to reverse a string, except in interviews. :-) The stdlib has a full (and good) HTTP server with HTTP/2 and TLS support, HTML templating, excellent I/O support, compression, even image encoding/decoding and drawing. That said, the container types are pretty sparse, but that may change a bit now that generics are here.

What, apart from string reverse, do you miss for real projects?


The fact that it's a bunch of top level functions also makes me think of PHP from the early 00s, only I was able to do more with things then tbh. Just feels super archaic.


Not that person, but I'll call out a few issues I've had with the go stdlib:

First, it has a lot of useless packages you typically wouldn't use, like "log" and "flag" (which work, but are way worse than third party alternatives like logrus and pflag), but also like "syscall" (as it says 'deprecated, use 'golang.org/x/sys' instead), "image/draw" (nope, you wanted 'golang.org/x/image/draw' usually), "path" for working with paths (you wanted "filepath"), "net/rpc" and "rpc/jsonrpc", "plugin" (almost always a bad idea), a chunk of "strings" (use "golang.org/x/text" for proper unicode support) and so on. Some of those are marked deprecated, most of them are not, and are just waiting for someone to accidentally use them.

That's issues I have with the stdlib and not stuff I'm missing though... Though I guess I really do miss a good logging library, or at least interface for external packages to implement so I can plug in logging libraries without rewriting the world.

One thing I do find missing frequently is a reasonable set type with the ability to do things like basic set operations (intersect, diff, etc). I constantly have to write ad-hoc for loops in go to do set operations, and it's verbose, non-obvious what the code does, and easy to get wrong.

But honestly, the main thing I'm missing isn't actually a package, but more about error handling for the stdlib as a whole, which is more a language issue. I really wish I could know what possible errors stdlib functions returned without, fairly often, having to read huge chunks of stdlib code to determine that.

Perhaps 40% of the stdlib documents the error type it returns in a message (like 'os.Chdir' always returns '*os.PathError'), but for the rest, good luck. Want to figure out what errors you might have to check for 'tar.Writer.Close()'? Well, the docs says "returns an error", the interface is "error", you have to read hundreds of lines of code to figure out the possible concrete types it could be. Maybe 15% of the time, you end up having to string-match on error messages because the error var or type is unexported.


> interface for external packages to implement so I can plug in logging libraries without rewriting the world.

Well, it's structural, so you don't need other packages to implement an interface rather you need them to accept an interface. That also makes it clear it's a bigger ask - you're not asking a dependency "please also do X" but instead asking "please never need to more than Y".

> Want to figure out what errors you might have to check for 'tar.Writer.Close()'? Well, the docs says "returns an error", the interface is "error", you have to read hundreds of lines of code to figure out the possible concrete types it could be.

The concrete types it could be are unbounded, because `tar.Writer` wraps arbitrary `io.Writer`s. If you need multi-pathed error handling (usually people don't and are just making it out of habit!), worry about what things can do, not what they are.


> If you need multi-pathed error handling (usually people don't and are just making it out of habit!), worry about what things can do, not what they are.

When I'm looking at an error, it's typically for one of two reasons:

1. To set a correct status code, such as http 5xx (internal server error, our disk flaked) or a 4xx (user error, you gave us invalid input).

2. To provide a better error message, such as to localize it into an error string.

If you're building CLI tools for yourself, sure, every error is fatal and you can read english so you don't need either of those. For most go projects, both of those are relevant concerns for a large number of error paths.

Go's type-system does not help you at all.

Speaking of...

> Well, it's structural, so you don't need other packages to implement an interface rather you need them to accept an interface. That also makes it clear it's a bigger ask - you're not asking a dependency "please also do X" but instead asking "please never need to more than Y".

Yup. That is a big problem. That's the root of the error problem too, where every package returns the stdlib error interface, which is a tiny subset of what you usually want.


> To set a correct status code, such as http 5xx (internal server error, our disk flaked) or a 4xx (user error, you gave us invalid input).

For this it's simple to wrap them at return site in something that offers `HTTPStatus() int` and check for implementing that interface, not any concrete types, in your handler.

Also, those error paths should be dangerously hard to mix in the first place, you shouldn't be letting invalid input anywhere near the disk to begin with.

Re. logging interfaces, I think you've missed the point. You want everyone to accept narrow interfaces so you can use the logger you want. You also want everyone to return wide error interfaces so you can categorize the entire universe of possible errors as you want. In the end this isn't a technical problem, it's an "I want everyone to cater for my use case" problem.


> For this it's simple to wrap them at return site in something that offers `HTTPStatus() int` and check for implementing that interface, not any concrete types, in your handler.

It's not though, the return site is inside the go stdlib. I cannot annotate it with new methods.

The only way to figure out how to translate all errors (whether to status codes or to other readable messages in localized languages) is to read the code and figure out what errors it might return.

> you want everyone to return wide error interfaces so you can categorize the entire universe of possible errors as you want. In the end this isn't a technical problem, it's an "I want everyone to cater for my use case" problem.

This is a technical problem. In Rust, libraries define error types and return "Result<T, MyErrorType>", which lets a library author decide what errors are interesting or not. If I think they have not classified an error that is useful for a caller, I can file an issue.

In java, exceptions have types, and I can know what types of checked exceptions a function might throw, and can similarly ask for more specific exceptions, or modify the library to provide them.

In go, _every_ library, due to go's error handling idioms and some mis-features of nils/type-inference, returns the most useless error type possible, the 'error' type, and I have to constantly read docs or code to figure out what types it might be.

I don't see how this isn't a technical issue with the language that, at the type-system level, it makes it an anti-pattern to return concretely typed errors in a way the type system can recognize them.

I don't agree with how you're characterizing what I'm saying as being "catering to my use-case".

Do you just never actually need to classify an error? Is it somehow weird to want to be able to provide a localized error to a user? Doesn't everyone have these problems too?


> It's not though, the return site is inside the go stdlib. I cannot annotate it with new methods.

    type httpClientError struct { error }
    func (err httpClientError) HTTPStatus() int { return 400 }
    func (err httpClientError) Unwrap() error { return err.error } // if needed
It's not even anything special around `error`, Go's entire type scaffolding is built around doing stuff like this.

> Do you just never actually need to classify an error?

Infrequently, and virtually never for errors types I didn't write myself (other than a tiny number of sentinels like UnexpectedEOF or DeadlineExceeded).

> Is it somehow weird to want to be able to provide a localized error to a user?

Yes, it's unusual for error details (rather than e.g. outcomes) to be localized for display directly to non-technical end users. This is also true of exception messages in Java. General-propose desktop client software is rarely written in either language.

I think you're too focused on the specific issue to see my general point about interface size.


You've constructed 'httpClientError', but how do you end up using it in your program? You have "pkg/httputil" or whatever, and the methods in it return "(Response, error)", not "(Response, httpClientError)".

Even within your own program, you now have to read the code in "httputil" to understand what possible error types can be returned.

It's idiomatic to never return concrete error types, whether from the stdlib, or third party libraries, or even methods within your own program.

Even for types you do write yourself, you still have to either memorize what errors each method may return, or you have to constantly refer to docs or source code reading.

Clearly you think this is fine and go's type system is good enough for your use-cases, but every larger go program I've worked with, error handling has been painful since the errors are effectively untyped.

I assume we must have worked on different types of go projects if you haven't run into pain with this.

> Yes, it's unusual for error details (rather than e.g. outcomes) to be localized

I absolutely agree that it's outcomes which are localized, but to determine _outcomes_, you have to classify errors. If the _outcome_ is "File doesn't exist", that's a different error than "permission denied", so you need to classify. But the type you have is "error", so you have to constantly refer to docs.


As I already mentioned - Go has largely unbounded error types, not because `error` is a small interface, but because of how many interfaces get nested (a tar.Writer wrapping a gzip.Writer wrapping an hdfs.Writer etc.). If you don't like this, fine, we'll have to agree to disagree about the value of checked exceptions vs. massively leaking implementation details.

Nonetheless, Go does provide ways to check whether an error either is or can do what you want, and ways to annotate errors with logic specific to your program. An `httpClientError` is an `error`. When you get an error from a source you want to treat as a 400, you wrap it and return it, as an `error`. You use `errors.As` on it as a concrete type, or an `interface { HTTPStatus() int }`, to use the method you've added.

Regarding localization, which is a significantly different problem - the outcome is e.g. "file can't be opened". It's hard to write good error messages based on the language's error messages but this is not a Go-specific problem at all. Either you constrain your operations to the point you can bound all your error types, or you don't and report the outcome + raw message instead of trying to localize causes. And yes, this is an unusual space to be using Go or Java.

Since you mentioned Rust, we could also consider how it solves the problem - `Write` returns a `Result<usize, io::Error>` - `io::Error` has a (almost uselessly long and yet still) non-exhaustive `ErrorKind` - the last of which is `Other`, "used to construct your own Errors that do not match any ErrorKind." I.e. even in Rust's type system, they punted because otherwise you can't easily compose anything.


The main thing that irked me though is I had to spend a whole day doing a simple "take this complex object and use it as a hash key" in an idiomatic way other than just cheating and using the stringified version of the object as the key.


Why do you want a string reverse?

- Do you want to reverse bytes, or codepoints?

- Do you want to reveres codepoints, or grapheme clusters?

- Do you really want to reverse grapheme clusters, or do you want to reverse some grapheme clusters while leaving e.g. sequences of control characters in the same order?

- Do you really want to reify any of this rather than iterate backwards in the existing memory?


I think most people are expecting something along these lines:

C++: reverse(str.begin(), str.end());

Dart: str.split('').reversed.join();

Java: new StringBuilder().append(str).reverse().toString();

JavaScript: str.split('').reverse().join('');

PHP: strrev($str)

Python: ".join(reversed(str))

Rust: str.chars().rev().collect()


Yes but why? Those expressions do wildly different things both in terms of language semantics and in terms of observable behavior and most of them haul in some heavy additional machinery from the language. Perhaps where it is present, this is a case where stdlibs have implemented it because it's easy to implement, and not because it's actually useful.

(What do I mean by wildly different things?

C++: Swaps the string's contents in-place, and probably breaks any multi-byte code units unless you've got a parameterized std::string at hand.

Dart: Makes a new string but has to round-trip via an array, because... it doesn't have a string reverse? This seems like a really bad argument for your side!

Java: Reverses codepoints, but the fact you have to round-trip through a StringBuilder to handle this is also telling.

JavaScript: Same comments as Dart, but I believe this is broken, it will reverse surrogate pairs incorrectly.

PHP: Good luck figuring out what this does depending on your platform, locale, and moon phase.

Python: Another codepoint reverse, again not via strings but a lazy sequence, and also not even idiomatic - use `str[::-1]`.

Rust: And finally again... not a string reverse.

You want a Go slice reverse? You can get a perfect one post-generics.)


And reversing by codepoints is still wrong. It wrecks multi-codepoint sequences like flags.

https://go.dev/play/p/IsZBLqi7--1


Not sure what you mean. The types consumed by an API are declared (though they might be interface types), and reading the godoc will give you a good overview of a package.

When would you want to look for all implementations of an interface? Is this something like an abstract syntax tree?


The interface defines the behaviour required at the point of use - that’s the point of them. You should not need to know which types implement an interface and if you do things are deeply broken in your codebase.

I’ve developed large Go codebases and never had this problem so your last sentence is false. In addition this is not an issue other go developers I’ve spoken to have ever worried or talked about.


> structs / types not explicitly declaring which interfaces they implement is a real and unaddressed problem

Isn't it a feature? You can have a "writer" as long as it can "write", and then use that writer anywhere where a function expects a writer?


But do you obey all of the semantics of the writer interface which aren't expressed in code?


> However, the issue this brings up about structs / types not explicitly declaring which interfaces they implement is a real and unaddressed problem, especially in large codebases. The only tool that I'm aware of that finds implementations is GoLand, at steep JetBrains prices.

So are you trying to find implemented interfaces or interfaces' implementors?

Former: In Vim I can use `:GoImplements`, which internally calls `guru` I guess.

Latter: `gopls` supports this.

I agree it's still a pain that one can not tell directly from code what interfaces a struct implements tho.


Every major editor I can think of has a way to find out what implements an interface using guru (https://github.com/golang/tools/tree/master/cmd/guru). In VSCode, it's right-click -> Find All Implementations. vim-go has :GoImplements. Using go-mode, emacs provides go-guru-implements.


> However, the issue this brings up about structs / types not explicitly declaring which interfaces they implement is a real and unaddressed problem, especially in large codebases. The only tool that I'm aware of that finds implementations is GoLand, at steep JetBrains prices.

Alan Donovan's guru tool could do this, but it kind of broke with modules and it was never updated and deprecated in favour of gopls. I don't know if gopls added this yet (I never really found a use for it).

I don't think it's very hard to write a tool for this though; parsing Go code is fairly easy and the stdlib provides a decent API for it. I think you could have a functional tool in a day if you wanted to, although without any caching it might be a little bit slow on larger code bases.


It's not great, but I use this pattern to check / enforce interface membership.

  type Bar interface {
     BarMethod(int, int) int
  }

  type Foo struct {}

  // Error: Foo does not implement Bar (missing method BarMethod)
  var _fooImplementsBar Bar = Foo{}


I highly disagree with the interfaces criticism.

Firstly You can literally just use one of the dozens of go lsps or code tools to search for API invocations to find what structs are passed/called into it. More importantly if you need to know you've written bad code. The entire point of an interface is that you SHOULDN'T need to know the underlying type. If you do you've violated the entire point. Just pass concrete ones. I've written Go for years and never had a problem with this, even in large open source projects like Kuberenetes.

Secondly, the criticism about flipping return values order/meaning isn't a criticism of interface being structurally typed (https://en.wikipedia.org/wiki/Structural_type_system). If you return int, int and the second int "should be even", you should have defined a type "Even" and returned int, Even*. Systems which are structurally typed can demonstrate functional extensionality and (https://github.com/FStarLang/FStar/wiki/SMT-Equality-and-Ext...) and check whether you've flipped the arguments, which would be a more valid criticism (but such checks are expensive and conflict with compile time requirements). Also Java has the same problem, if you define two interfaces with the same method signature and a single class implements both you can't disambiguate.

Thirdly, the structural typing has a huge advantage, namely looser coupling and more tightly defined interfaces. If you follow the "accept interfaces return structs" go idiom, you'll see why. An open source library that does so leaves their returned structs open to be used by consumer code, that itself uses interfaces, without modification required. This means most go code has small, tightly defined interfaces, where every function on the interface is invoked in the relevant function.

For example if you have a library with this definition:

type Baz struct {}

func (b Baz) Foo(){} func (b Baz) Bar(){}

I can use Baz in my code like so:

type Fooer interface { Foo() }

func DoSomething(f Fooer) { }

And use the underlying library, while being decoupled from it, without having to modify it.

Fourthly: You can explicitly say a type implements an interface...

* A good study: https://blog.boot.dev/golang/golang-interfaces/

* In Idris we would do:

even : Nat -> Bool even Z = True even (S k) = odd k where odd Z = False odd (S k) = even k

int -> even doubler a = 2 * a


You can explicitly declare that it implements an interface:

https://go.dev/play/p/VCcrSXT59w0


I think you forgot the "hasA" type on the var line. Fixed: https://go.dev/play/p/0Amm7nd1Av4


I don't write Golang but honestly that was one of the features that interested me the most. Automatic type compatibility. I get the criticism as you will depend on tooling to get some expected convenience. But if you are used to void *ptr anyway...

To be fair, most APIs need documentation and code is just plainly not enough. At least if we are talking about specialist interfaces that aren't just another web framework.


Doesn't the official language server have support for this?



I'm still baffled by their decision around date formatting.

https://www.godateformat.com/


I find 15:04:05 on Monday Jan 2nd 2006 a lot easier to remember than all those strftime %-verbs. I certainly don't see how "%B %e, %Y" is any better than "January _2, 2006". %B for what? Bonth name? And %e for "d for day plus one so %e".


1999-12-31T23:59:60 would make it easier to remember what the constants are.


The way I remember it is that it counts from 1 to 7:

   2006-01-02T15:04:05Z-0700
It's unfortunate that the year is 2006 and sandwiched between the minute and TZ offset, but this keeps the day at Monday (1st day of the week, for many anyway) so that's nice.


I wrote https://golangti.me because I never remember _any_ of the formatting types (besides Excel (which isn't as extensive)—d, dd, ddd, dddd, m, mm, mmm, mmmm, mmmmm...).


This makes no sense to me. First time I’ve been confused about date formatting.


>The tried and true approach of providing a compare method works great and has none of these drawbacks.

Agreed! https://pkg.go.dev/sort#Slice is wonderful. (Added a bit after this article was written, I think.)


Lets see what the Go doc example looks like:

  sort.Slice(people, func(i, j int) bool { return people[i].Name < people[j].Name })
In Python one might write:

  people.sort(key=lambda person: person.name)
Or in Rust:

  people.sort_by_key(|person| person.name); // sort_by is also an option...
I think it's worth calling out exactly what is happening in the Go example:

- We create a closure that captures the people slice

- We pass the people slice and the closure to the Slice function

- The Slice function mutates the people slice, and because the closure captured the slice it sees these mutations too

I get why the Go team wrote sort.Slice like that, and it was perhaps the best they could have done with the language features...But I think we're going to have to agree to disagree on how wonderful it is compared to other languages ;).


No disagreement, having used Python a lot and finding it much nicer for this case. My compliments to Go on this were relative to the just how tedious the “implement this sort interface” method was that TFA was describing. sort.Slice is definitely still pretty rote (I think I write the same Less() function 90% of the time?), but it’s at least fewer characters of rote!

I’m looking forward to generics improving this too. (e.g. https://github.com/golang/go/issues/47619#issuecomment-91542...)


It's worth noting though that Go's way is just 40 characters more than Python with an inline comparison function and Go's verbosity.

If I need to reverse the order, it looks easier to do with Go (just reverse the operator) than with Python and Rust way (I guess both have something like an "order" additional parameter).

Rust and Python both feel more elegant but I actually like Go's way.


Python has a `reverse=True` kwarg for doing this.


Go's method is like that because it didn't have generics, but it does have the advantage of allowing you to sort more complicated things, e.g. indexes into other data structures, or computed values.

Sorting by key is a special case (admittedly the most common special case).


My thoughts on his points, from someone who really likes Go and has used it heavily on small and large projects (1M LoC):

1. Probably a matter of taste, but I love this feature, just because of the lack of noisy public/private keywords everywhere that you see in Java et al. It also means you can tell from a usage (not just the definition) that something is exported, which is often useful.

As far as renaming goes, either rename the definition and see where the compiler complains, or get your IDE to do it (I use GoLand, but good things are said about gopls).

As for his example, the idiomatic way to write that is either just call it `usr` or `adminUser`, or use `user := &user{}` which is valid (if a little confusing).

2. This is a feature: it allows you to define interfaces only where you need them (on the consumer side), and you define the interface with only the methods you actually need. This means that when you add a bunch of new methods on the implementation, you don't need to change all the consumers. Go interfaces are amazing.

The downside he discusses almost never happens: it's surprising, but even in large projects I've never had structs accidentally implementing interfaces or a IsAdmin method being implemented with reversed polarity by accident.

3. Definitely has its downsides. Tooling helps find unchecked errors. Though I've found the biggest downside to explicit errors is the verbosity. You do get used to it, and the explicitness is at least clear.

4. There are a couple of "magical" things like this, but they're well known and documented, and simple to fix if you run into them. I love the fact I can just name a file foo_test.go and add TestFoo methods, and "go test" finds them automatically.

5. I have not found this to be the case, and in the rare cases it does happen, the compiler tells you loudly and it's easy to fix.

6. Yeah, this is a slight pain, but the semi-official "imports" package (golang.org/x/tools/imports) fixes it up, so you just run generated code through that (and it auto-formats the code as well). It's a couple of lines of code. See: https://github.com/benhoyt/prig/blob/2df1b65a2bdf34c10bb5e57...

7. Yeah, I wouldn't mind a ternary operator. Easily misused, which is why they didn't add it, but it would be really nice used judiciously, rather than the 4-line if-else block.

8. Fixed by sort.Slice, which avoids the need for Len and Swap (and even more so by the new generics "slices" package, coming soon). I guess this was added after the article was written?

9. Fixed by "Go modules", which is really well designed and works well (though opinions differ).

10. Fixed with generics being added in Go 1.18. And generic helpers like "slices" and "maps" packages coming soon.

11. Yeah, slightly annoying for newbies, though as he mentioned, tooling tells you. I do like the control you (can) get over allocation and memory management with Go slices.

As far as his summary goes (eg: the type system getting in your way for large programs), I have definitely not found that to be the case. The author doesn't like Go, and that's okay! I don't like Java. :-)


On point #2:

I don't know anything about Go, but I've spent a lot of time working in a lot of languages. Structural typing is a bad idea. There are a few, limited cases where it's necessary, e.g. we have it in TypeScript because TS ultimately has to work within the limitations of JS. But if you don't have those sorts of limitations, intentionally implementing structural typing in your language is borderline negligent.

Interfaces don't just define a bag of method signatures. Interfaces are semantic, too. An Employee, a Gun, and a ClayPot might all have a method named `fire()`, but they all mean different things. But with structural typing, we can load our Employees into a Kiln and give HR a Gun to downsize the company, and nothing will stop us.


The upside of structural typing, with some help from Go idioms, is that it encourages composition and narrow types. A type like bytes.Buffer implements Reader, Writer, ByteWriter, ByteReader and another 5 or so interfaces but the call site can declare which set of methods it needs.

The downside of nominal interfaces seems worse: you have to import the interface which can lead to awkward situations like cyclic dependencies and type-only packages (a la Haskell). In practice, the extra friction of nominal interfaces also seems to encourage wide interfaces in contrast to Go's narrow interfaces.


I’d like to get into go but in the past I’ve always been burned by not being able to quickly refactor my code.

If I want to change the contract of get_kittens so that it returns a set instead of a list, I found it quite tiresome to then go to all the call sites of get_kittens and change their types to match.

What was I doing wrong?* Perhaps there’s a cleverer tool out there that can infer and implement these type changes for me, automatically?

* using vim? [joke]


Although go has many deficiencies, this is not one of them. When you use "var" or "val", it works like "auto" does in C++.


I also use Vim.

1. Use `:=` whenever you can, so types are inferred by the assignments/allocations 2. Alternatively query all references from `gopls` and put the results into quick fix list, then `:cdo`


This is true for all typed languages though..


No, type inference avoids this issue.


Which Go has. https://go.dev/ref/spec#Type_inference

(Not to a level something like Typescript has it, but I'll argue that's a win. Typescript inferred typing can be hard to follow.)


Why isn't Go good for large projects? Kubernetes is an example of a huge project built in Go


This is stock example. Anything else?


Excluding any lines that aren't Go code, and from projects that I can recall off the top of my head: Kubernetes = 3.87 million, Go = 1.75 million, CockroachDB = 1.65 million, TiDB = 725k, Consul = 375k, Nomad = 375k, InfluxDB = 375k, Mattermost = 370k, Vault = 345k, rkt = 320k, Terraform = 275k, Grafana = 265k, Ethereum = 245k, Gitea = 225k, Dgraph = 205k, MinIO = 200k, Rclone = 200k, etcd = 185k, Hugo = 120k, Prometheus = 120k, Syncthing = 110k, Traefik = 110k, Juju = 105k, Perkeep = 100k.


Ok, fair enough.

Love Syncthing, didn't know it was written in Go. Though I wonder why they wrote MacOS client in Objective-C instead?


Agreed with the article, though obviously it’s a bit dated (especially around generics and package management).

My take is that Go is basically the new Java, with fewer abstractions and faster compilation. Although, the pre-Java 8 Java, before Java started to get a bit functional.

Like Java it’s a practical, imperative, statically typed, garbage collected language with very good performance. Also like (pre-Java 8) Java, it’s very verbose, doesn’t allow for much “elegance”, and many find it not very fun to write. But it is a pretty decent language for getting shit done.

Overall, I don’t really enjoying writing Go, but it’s not the worst either. I’d code in it if necessary, but wouldn’t chose it for a personal project. I just have more fun and am more productive writing code in concise, mixed OOP/FP languages like TypeScript or Scala, even if they don’t compile as fast.


It's the new Java 4. Eventually they'll add the missing stuff and it'll get current with Java 6.


I would really like to see a benchmark on compilation speed. Sure, Go’s compiler is really fast by not doing any fancy optimizations - but.. have you seen javac’s byte code output? It barely does constant propagation, because it can get away with it due to JIT. So if anything, javac just starts up a bit slower, or the build tool does something at first start but otherwise java programs literally compile in a blink of an eye.


I’d like to see that too, share it if you find it! My personal experience has been that the Go projects I’ve worked on have compiled a lot faster than the Java projects I’ve worked on. However, it’s hardly a 1:1 comparison, the Go projects were much newer and smaller than the Java projects, would like to see some legit benchmarks.


Surprised you compare it to pre-Java 8 considering functions have first class support in go.


First class functions are a lot less interesting without functional data structures, with methods like map, filter, reduce, etc. These were added in Java 8, but Go has always been against them. This may change with generics, people can certainly write their own now, but I’m on the fence about whether they’ll catch on. I think they still won’t be overly nice to use in Go because:

- Go’s lambdas are extremely verbose. “Concise but clear” is a big part of what people live about functional data structures, Go won’t have the concise part

- Go will still have almost no support for immutability, which works beautifully with functional data structures

Code like this is nice to write:

users.map((user) => user.id)

While code like this isn’t:

users.map(func (user User) string { return user.id })


The Scala ecosystem is super cool, once you're able to groc all of the neat stuff happening, sort the good tools from the bad, etc. Love http4s, scala-js, scalajs-react, doobie, cats, etc.


I just leave this here: https://github.com/golang/go/issues/49383

After such public disregard to the communnity and contributors as a whole, the talk about good or bad has no meaning until they learn the basics. For example how to work with community, and the fact that you have to provide your phone number in order to fix urgent bug or implement some feature is a plain stupid(or rather malicious).

Just imagine you've spent your free time working on the fix or feature, and instead of getting appreciation or sometimes bounty or just nothing, you're being "charged" to contribute. Yeah, they really think it's normal that contributors have to give up PI to the advertisement company that were accused of violating privacy many times before. They basically treat contributors, tech-savvy users who provide free labor, like their usual consumers. Just think about it for a second, this is insane.


As a new Go user I seriously don't get the hype. It feels like C with some (not many) niceties thrown on top, that's not what we expect from high level languages. I am still waiting for the tada moment, hope it comes.


I don't think there is a tada moment but for me that's ironically enough what made the language deserve some hype. I like C, I never liked sophisticated languages that impose too strong of a style on the developer, a garbage collected, nice C with good performance and practical choices is pretty much what I wanted for a really long time.


I don't want to spill the beans here, and well: you figured it already out by yourself.


What are you using it for?


Crud APIs...


IMO it’ll be slower to write crud apis and there are some missing batteries you have to look for, it’s fairly verbose but I think some of that will change with generics and hopefully more pattern matching.

On the flip side when you deploy it, it will like just work and run smoothly. Also very easy to add full integration tests as you can create a mock http server at runtime, paired with some db tools (go migrate) it’s very easy spin up a whole env and test it. It’s also very easy to get at lower level things like headers and cookies without it getting in the way. Also much of the web middleware is interchangeable or a few lines away from an adapter as there is a common interface in the std lib.


OK thanks for this, the easily mock thing is indeed very nice to have. And maybe Generics will improve things. I don't get why there isn't a function to remove element from a slice (there's some append[:i],[i+1] trick instead) why wouldn't this be wrapped in a stdlib function? call it delete or something. I keep hearing good things about the stdlib - what am I missing? It's that do it yourself mentality I really don't like, and taking huge compromises on readability to preserve low language keyword count (like the lack of private keyword / capitalization the article discussed and the consequences) and then calling this whole setup "simple". There's nothing major but many many small things I think that make it a subpar experience. I don't care that much about performance and concurrency though which I think are Go's strong suits.


A lot of these little irritations were due to a lack of user-accessible generics. Now that generics are available, they're available in the exp/slices package: https://pkg.go.dev/golang.org/x/exp@v0.0.0-20220609121020-a5...


What did you write CRUD APIs in before Go?


Rails. A bit of Django and Node. All 3 were better in my eyes but as I said in another comment I care very little about performance and concurrency so maybe I'm not Go's target audience.


Title needs 2016


another person who can’t get over his java stockholm syndrome in three (!) years

one particular thing that tells that is the attitude to interfaces:

while in java (and most languages) interfaces are used to tell which contracts a class implements, in go it’s reversed. you must declare interfaces to _require_ certain contracts, for arguments in your functions

for example:

type interface Operator { Operate(int, int) int }

func IntOparation(a, b int, op Operator) int { return op.Operate(a, b) }

this is a major difference highlighting the ownership boundaries: * when I write a package and rely on a 3rd party contract, instead of referencing it and adhering to it, I will copy-paste parts that I need to my package and be independent


> if I name my source file i_love_linux.go, it won’t get compiled on my Mac

What the fuck?


There some file name conventions in place in Go [1]. Files ending in *_test.go are going to be run dufing `go test ` invocation, but not compiled during production build run. In general, last parts of file name reflect "tags", or a platform that this file should be built for. So, in the case of *_linux.go file will only be compiled when it targeted linux platform. Allows to have a file say file_windows_amd64.go and file_linux.go that have functions with same signature, but only one will be picked up for a target platform. Sort of like #ifdef , but at file-name level.

There is a way to specify what file is actually targeting inside the file, through //go: "pragmas" too

[1] https://stackoverflow.com/questions/25161774/what-are-conven...


Others already explained why.

Now a bit more background, this goes back to C and C++, and is considered a best practice to name translation units as name_os_arch or similar pattern, with the header file being only name, instead of #ifdef spaghetti.

This is actually one of the few things I think Go did right.


Explanation from a previous discussion - https://news.ycombinator.com/item?id=16414435

...seriously wtf


In 5 years of writing to code this has never prevented me from naming a file what I wanted to name it. I enforced conventions are nice, when I need to look at what’s different between platforms I can very quickly do so in any large repo.


Yeah, "If I do <some obscure thing> in <chose any language>, and it doesn't work how I like, I don't think it's good"


Naming a file is an obscure thing?


__main__.py? PRN? index.html? "This filename does something special" is hardly a situation unique to Go.


__main__.py clearly stands out with its underscores. index.html is a historical accident. And they're both just single files, not a glob.


Searching around a few blogs mention _linux.go files are only compiled on Linux. But I see nothing in the official documentation, will at least in my quick searches.

It will catch someone out who uses BSD as an acronym for something in their domain model (Bulk Sales Discount?) then xyz_bsd.go doesn't compile.


https://pkg.go.dev/cmd/go#hdr-Build_constraints

  If a file's name, after stripping the extension and a possible _test suffix, matches any of the following patterns:

  *_GOOS
  *_GOARCH
  \*_GOOS_GOARCH
  (example: source_windows_amd64.go) where GOOS and GOARCH represent any known operating system and architecture values respectively, then the file is considered to have an implicit build constraint requiring those terms (in addition to any explicit constraints in the file).


Idiomatically in Go that file would be named `xyz-bsd.go`. All special filename behavior is triggered by `_suffix`.


Why does Go get so much hate here? Almost all front page articles are about how Go suCkS mAn

I know Go isnt great or perfect, but still, why so much hate? I seriously want to know


To be 100% fair, this post is a full 6 years old now.

But also, I don't think it's specifically hate, it's more of a reaction to the overwhelming wave of posts here (and basically on every programming forum) from around 2014 to 2018.

Go was so hyped it was unavoidable that if you were starting a project, dozens of comments would be shouting at you to use go.

Some posts are people finally getting to say, "I told you so, but I was against the crowd a few years ago" Some posts are people saying, "Go doesn't really fit this use case" Some posts are people just academically sharing the language features you don't get when you choose go.

Basically, in my opinion (and as a developer of a large go codebase that I really love), thousands of people hyped up go as a silver bullet or a "near-perfect" language. This is obviously not true, go has many downsides. When you tried to bring them up before, you were downvoted and pushed aside for the hype. Now that people are maintaining legacy go code, there's more appetite for these conversations about go's tradeoffs.


There's definitely valid criticisms, but a lot of it seems like hate. But I also agree that it's probably a reaction to the earlier hype wave.

I especially find it funny that people assume that people like Rob Pike and Ken Thompson who have published research papers on computer science forgot or misunderstand modern language features. They purposely made their language this way, for better or for worse. They never stated that they were trying to make the next Java. And all of the successful projects that have been released so far written in Go is proof enough that it seems they know what they're doing.

People complain online for a language they won't use instead of using the languages that are "superior" to make useful modern software.


For many years Go has been displacing C/Python/PHP/Ruby/backend JS in situations where it's a clear win or at least the tradeoffs in tooling, speed, type systems, distribution etc. can be expressed in terms of agreed-upon technical priorities.

Now there's a lot of Go code in the wild, projects people want to use or extend and programmers who will choose it by default, and it's starting to displace C#, Java, and C++ where the comparisons become a lot more preferential and vague. Do you want a faster GC or deterministic allocation? Nominal or structural interfaces? People fear change, especially in a field where change is only loosely correlated with improvement.


Do you think Go is really starting to displace C#, and Java though? I rule out C++ because I really think rust is the one aiming for that spot. But Go for C# and Java? Hard to say until we see Go start to take over more of the business domain applications within enterprise IMO.


I mean, for Kubernetes, Go literally displaced the prototype in Java. And the surrounding tools that are all now in Go, would likely have been in Java instead. You could say it's been successful in part because it's not Java and therefore Go "grew the market" instead of displacing anything, but I think that's pretty weak - some kind of DIY cluster management was coming, and the state of things is that Go snatched it from Java.

I think Go is quietly taking over some business domain applications, reminiscent of Python ca. 2003 a lot of companies I see are using it for a few key components that benefit heavily from memory savings of value types and/or easier naive concurrency, but they're relatively quiet about it. There's also a small but significant cohort of junior devs today who learned to program by making games on fantasy consoles or homebrew hardware projects, want to keep working with those data-oriented patterns they learned C and C++, and Go supports that style a lot more idiomatically than Java. As much as I sometimes rant about "kids today", more of the current generation know the true value of a MB or a ms better than that of 10 years ago (Rust is helping a lot here, too).


2016. Generics have been added.


> Go doesn’t have exceptions. It uses multiple return values to return errors. It’s far too easy to forget to check errors...

Yes, because it is easy and predictable to track exceptions in nested try catches and hidden control flow.


>There’s no ternary (?:) operator. Every C-like language has had this, and I miss it every day that I program in Go. The language is removing functional idioms right when everyone is agreeing that these are useful.

And everyone agreed so hard that it was removed from almost every modern C replacement (Rust, Nim, Zig, Elixir, Kotlin).


Rust has if/else (ternary) and match (N-ary) expressions, so it doesn't need a separate ternary operator. All of the other “C replacements” listed (which are a weird list for that description, especially Elixir, but whatever) have at least if/else-expressions, which, again, are ternaries.


Yeah if I can have decent pattern matching I can live without some other syntax sugars.


Have you met my friend.. erlang ?


Indeed, big fan, though it's rare I get to use it.


True. In Kotlin you can go

    val foo = if (bar) "this" else "that"
and you don't need two separate assignments.


For comparison, in Go it would be:

    foo := func() string {
        if bar { return "this" }
        return "that"
    }()


Bit of an odd use of an anonymous function IMO. Normally I'd write that as:

  foo := "that"
  if bar {
      foo = "that"
  }
Unless the assignment of "foo" is expensive, then you'd assign it in an else.

If you really want to, you can do it in a single line too:

  foo := map[bool]string{true: "this", false: "that"}[bar]


These are rather nasty workarounds, to be honest. Instantiating a map out of the blue is clever, but not very readable (not to mention potential performance concerns eg. if it's code executing in a large loop), and something I'd definitely call out in code review. Note that computing "this" and "that" could by itself be costly, and one of these values is guaranteeed to get discarded.


I've used the map thing a few times, but sparingly. Mostly for things like setting something based on the value of a boolean commandline flag and the like where the performance impact doesn't matter and keeping it on one line offsets the "ugliness" of the construct. You should indeed avoid it for most other things.


I was simply giving an example of only using a single assignment, as the parent stated:

> and you don't need two separate assignments.

Although I do use it far more often nowadays than a decade ago. I've even mentioned it before https://news.ycombinator.com/item?id=30384835.


> And everyone agreed so hard that it was removed from almost every modern C replacement

They removed it because they made the regular "if" statement into an expression, so the separate "ternary" operator expression is superfluous. Rust et al made the "if" situation better than C; Go made it worse.


This is entirely off topic, but how are Elixir or Kotlin "modern C replacements"? Elixir is built off of Erlang and has influences from Ruby and Clojure, and Kotlin is a replacement for Java.

Rust, Nim, Zig, and Go are all fair game in that list, as far as I know being a C replacement is in the mission statement for the creation of all those languages.


I literally don’t understand why so many people believe Go is a C replacement. Like it is by all means in the first camp of managed languages with a huge runtime.


Replacement for C in many areas it is used for != replacement for C in all areas it is used for


Sure, as long as Node, C#, Java, Haskell are all C replacements as well. Which I am okay with claiming, as GC has no considerable overhead for most use cases, but go is not unique in any way or shape.


Go is much closer to C than Node, Java, or Haskell. (I won't make claims about C#, I don't know it well enough -- but I doubt it's as similar to C as Go is.)


All those languages have if-expressions, which is all ?: is.


Totally. In fact, if expressions are the very first example of “if” in the Zig manual

https://ziglang.org/documentation/master/#if

Also, Zig is so into expressions over statements that they plan to remove “function definition as statement”, which I think is awesome. In other words, conceptually function definition is a compile-time expression that creates the function.

Edit: here’s that Zig rfc:

https://github.com/ziglang/zig/issues/1717#


Ternary syntax was statement-oriented languages' compromise to get one bit of nontrivial expression syntax into the language. In Rust everything is already an expression, including if-else, so additional syntax isn't necessary.


Elixir has the if-else being an expression.

foo = if x, do: 1, else: 2




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

Search: