No offense to the author here ... but I need to vent some personal frustration. Every tutorial goes through these exact same motions for learning a new language. It sucks! Programmers can figure out what a variable is, or how to use an array or a map. Those things really don't change a whole lot between languages. What about how to import code from different modules? Best practices for organizing code? How your language differs from its competitors as far as usage. Testing practices (which often influence how your code is written), etc...
Learning Elixir was tough for me until I found Dave Thomas' "Elixir for Programmers" where he skipped all of the basic stuff that every programmer is assumed to know and went right to what makes Elixir unique and how to leverage it correctly. (https://codestool.coding-gnome.com/courses/elixir-for-progra...)
I have been hunting for the same thing for the Go language for years because frankly I haven't been able to grok the appeal. I am hoping to find a 'Go for Programmers' one day. I asked on Reddit a while back and was pretty much handed the "what are variables" document.
99% of the “learn programming language” literature is written by people who likely couldn’t do your job because they haven’t ever been programming.
This is why languages need good documentation.
It seems like it’s a booming industry though. I mean, there are entire sites like udemy where amateurs teach amateurs things they’ll never use in a real job, and that place, and others like it, seems to be doing just fine. Hell, every PACKT book ever written was worth more as blank paper.
Why do I care? I’m an external examiner, sometimes I need to read some of these god awful books to see what students are expected to know. I’m not sure why professors ever stray from the true CS classics, but they unfortunately do. Especially at the academy level.
> I’m an external examiner
> sometimes I need to read some of these god awful books to see what students are expected to know.
LoL
Here in India every kid de-facto refers to few content farm websites and while they are not outright wrong, there will always be subtle errors, as the content is contributed by other students only. And books - the textbooks are so unpopular and not even available on libgen.
Day by day, academia seems more like a drug cartel.
haha its worse than you realize. I was in a cafe in jakarta and I noticed this guy was giving another guy lessons in php. We ended up talking after and I mentioned I use vue. He wanted me to teach him how to use vue so that he could run a workshop where he teaches vue!
I feel like there's something a bit off about charging money teaching a skill that I haven't personally used extensively in a professional context. I guess others feel differently
Seconded. Especially for Go, because it's mostly a lowest common denominator of language features it's not hard to write the code itself. The problem is that the module system is different and had plenty of changes along the years, What I would really like is an up to date "learn how to handle Go project in 30 minutes" tutorial (initialize new project with all best practices, add modules, publish, fork&clone&submit&pr including 3rd party submodules, test, CI, profile, debug).
5. go get -u github.com/gin-gonic/gin to add gin (web lib) for your project. Same for any other package. The will be added to your go.mod and go.sum files
5.1 import "github.com/gin-gonic/gin" in your code to use gin
5.2 package may have more than one major version (tag). If you want to use package at latest tag 2 - you add '/v2' when you 'go get' the package (i.e. go get -u github.com/gin-gonic/gin/v2)
6. git commit && git push to publish
7. Not sure what to you mean by 'fork&clone&submit&pr including 3rd party submodules'. You may use vendoring but in most cases you don't need it as you have your dependencies in your go.mod && go.sum files. You may want to read more about vendoring and gomodproxy though.
8. Testing is pretty easy but I guess you want and article about best practices? I won't post any link as I find the topic as too controversial regardless of the language\stack
9. CI - not sure that does this have to do with the language
10. haven't done much profiling or debugging to fill you in.
I was almost with you but please don't share that repo. It's a terrible layout and the docs aren't accurate. The issues are full of Go community folk saying it's misleading and looks official but isn't.
I have had this issue with my current golang projects. I haven't found any clear standard for project layout. We have a bunch of microservices with grpc api endpoints, and some which are event driven. Has anyone found a better resource? As it is, even our internal projects are fairly inconsistent in their project structure, which is a bit annoying.
> Not sure what to you mean by 'fork&clone&submit&pr including 3rd party submodules'.
Sometimes a 3rd party library needs a fix, and you want to fork it, add your changes, and run with it like that until your changes land in the upstream.
You can replace with local or remote package (for example if you have published fixed version somewhere and what your teammates to use it too until your changes land in the upstream.)
> How your language differs from its competitors as far as usage.
Yes! I'm always tempted to ask "How is this different or better than XYZ lang?"
I don't have the time/inclination to learn every new fangled language that comes by, so it would be nice to have an easy way to understand what scenarios any given language is best suited for.
For example, every time I read about Go, I'm left wondering: How is this better than C or Java? Presumably it's better in some ways I'm not perceiving, otherwise why would it be built and used?
It would be nice to have a collection of resources for each language for whatever you have mentioned above. It'd be very helpful to just deep dive and learn any language than focusing on declaring variables and for loops.
One resource that atleast comes close to what you are talking about is the information at go101.org, not sure if you have seen this before. Especially the section on Go Type System and Concurrent Programming is really very good and they focus on how to leverage the feature correctly for the most part. Hopefully you like it https://go101.org/article/101.html
Would you be up for venting that frustration in to a git repo and setting the tone by making public exactly that type of thing for the languages you do know?
Articles with a title `Learn <programming language> in 5 minutes` are misleading. They should be renamed `Discover <programming language> in 5 minutes`. Replace the verb Discover by Introduce or something similar if you like.
There is a lot of difference between learning a new programming language and discovering one.
You will not learn any programming language in 5 minutes.
> There is a lot of difference between learning a new programming language and discovering one.
Yeah, discover just lasts the moment you stumble upon it. Once you sit down to learn the syntax and basics, you're learning the language.
- discover - find unexpectedly or during a search.
- learn - gain or acquire knowledge of or skill in (something) by study, experience, or being taught.
I don't think anyone is reading the title and thinking "Huh, I can learn _all_ of Go in just 5 minutes?!". The time is an indicator that you'll just scratch the surface, not become a master gopher.
This is neat, and a testament to the simplicity of Go.
I normally point people to this tutorial if they want to learn Go quickly, it is interactive (able to navigate straight to go playground) and very well done in my opinion.
As someone who's not written any Go, I found fasterthanlime's critique of the language[0] damning enough that I likely won't ever touch the thing. Maybe he's cherry-picked examples, but his article was thorough and technical enough to convince me that the Go mantra of simplicity is just surface-level.
How is this damning, exactly? It's a very specific set of library quirks that are kind of expected -- Chmod doesn't do much on Windows. Arguably, maybe Chmod shouldn't be in the core library if it can't do everything on every OS/architecture combination, but I am not sure that damns the language. It does the best it can, but doesn't deliver a POSIX overlay for Windows. (The complaint continues into a discussion of build tags, to make Windows-only code compile only on Windows. Again, what's the alternative here? If you want your program to do different stuff depending on the platform, there's going to be some sort of conditional to apply that.)
There is then a discussion about monotonic time. The author immediately knows they want monotonic time, but there is no method that specifically returns that. Instead, go embeds the wall clock and monotonic time into a time.Time when you create one with time.Now. It uses the wall clock time for display, and monotonic time to compute the difference between two timestamps. This is not what the author expected, but it ultimately allows them to do what they want. (There is then a complaint about how when you boot a Raspberry Pi without giving userspace any time to start up, the time is set incorrectly, and Go doesn't help. Well... yeah. You can add a hardware RTC if you want the time to be correct as soon as the i2c subsystem comes up, or you can wait for NTP, or you can live with the wrong time. Not sure how any of this involves Go -- it does expect the OS to do OS things for it.)
If you were going to write a damning critique of why to never use Go, it would probably be something like "this dumb library uses channels, sync.Mutex, and atomic.AddInt64 all in the same code and it deadlocks and runs out of memory" or "I installed the most popular ORM on Github and it prints colored text to the console when there is a database error rather than returning an error from the function". I think the linked article totally misses the mark on what problems people are likely to encounter with Go. Let's be honest -- 99% of programmers have no idea that their OS has a monotonic clock, and are quite surprised (and have no idea how to fix it) when start := time.Now(); time.Sleep(time.Second); fmt.Println(time.Since(start)) prints a negative number. But... with Go they don't have that problem. Pretty interesting.
I guess I ignored the section on resolving the long dependency graph, and that's important to discuss. The community could do a better job of splitting their clients and servers into separate modules (writing to InfluxDB should not require downloading the code for an InfluxDB server). (I do love using servers written in Go, though, because my integration tests can just start one up at the beginning of the test and talk to it. No setup/teardown required outside of "go test"; no extra dependencies to make developers install. It's nice!)
The language could do a better job of letting you choose "plugins" at runtime; if you use Prometheus for monitoring and not InfluxDB, it's not ideal to have a bunch of 'if UseInfluxDB { send to InfluxDB }' compiled into your binary. The alternative is to dlopen something that provides monitoring functionality; been there done that with Nginx and OpenTracing, and it's hugely painful. (There simply aren't binaries that work together; you can't wget a prebuilt plugin into your Nginx docker image. You have to build both from scratch.) Or, you can at least defer the monitoring library selection from library to application with an abstraction layer like OpenTelemetry. The cost to the code author is high, and it still doesn't let the operator choose the backend at runtime. Overall, this is something that a programming language should choose to take on, but it's also exceedingly difficult to get right. I haven't seen it done right, anyway; so all you have to go on is a list of ways to get it wrong.
It is annoying that there are 600 different logging libraries. But people are VERY particular about logs, and one person's treasure is another person's "that is so horrible I can't even use it". If there is a programming language where everyone uses the same logging library and that library provides structured logging, the log levels are [trace, debug, info, warning, error, fatal], has built-in support for sending to third-party services (Sentry), rate-based sampling, and metrics generation... then that sounds great to me and I'll definitely take a look. But for some reason, I kind of doubt it. People have a lot of thoughts here, and are very committed to their thoughts. In the Go world, libraries with wonderful authors let you inject your own logger, and it mostly works well. GRPC, pgx, Jaeger, etc. all do a good job here. K8s's client_go does a bad job here, but they're working on fixing it :)
Wasn't planning to get sucked into this discussion, but the article does raise some good points. I'll be very honest and say rants like this sound to me like a pre-rant for next year when it's "why I quit programming forever and became a beet farmer". To succeed at programming, you will have to endure in the face of minor problems with your tools. You will have to use those tools to program better tools, after all. And even then, it still won't be perfect, and you'll see a new set of problems to be mildly annoyed at. Such is life! Ultimately, for me, Go has supported my efforts to make stuff. I run it on STM32 microcontrollers. I run it on Beaglebones. I ran it on thousands of servers at Google. It just kind of gets out of the way and lets you turn your ideas into things that you can use. Who can complain.
Thanks for sharing this. I'm halfway through it, and it is indeed damning, if only mildly so (compared to, say, phpsadness). As the author says, the specifics are not the point, rather the overall picture they build: that simplicity in language design has its own price, and sometimes all it means is that the underlying complexity is passed on to you to handle in your program yourself; and the damning part is, Go does not seem to do a good job of making that process as smooth and clear as it could.
It sounds like you've made up your mind, but I might suggest reading less overwhelmingly biased critiques. That post makes incorrect assumptions about go, and then exhibits the result of their incorrect assumptions, and then brings up how, when making the correct assumptions in Rust, the outcome is different. Can you see how this is a flawed strategy?
Go isn't for everyone, and that's okay, but it seems kind of silly to make a judgement based off of a negative review hinging on a pretty poor understanding of a language.
Really? I'm halfway through "I don't like Go's abstractions over file permissions on different operating systems" and rolling my eyes.
When the author is talking about how great Rust's error handling is (by forcing you to decompose the Result type), they also forget to mention things like "the go linter that everyone uses will let you know that you are ignoring the error." It's not a "Rust is always safer" scenario like is implied, it's a deliberate tradeoff for a less-fussy compiler, because sometimes you're prototyping and want to ignore the fact that you're not handling everything perfectly. Yes, Rust has better typing, and Result types are a great pattern, but the article makes it sound like the Go situation is uniquely bad.
Similarly, with the complaint: "Rust Paths, however, are... arbitrary byte sequences. [...] See, there's no "path" type in Go. Just "string". And Go strings are just byte slices, with no guarantees what's inside." If you program in Go for a week, you know that strings are "just byte slices." If you want to see if it's got utf-8 contents, just run `utf8.Valid(mystr)` - it's in the standard library, easy to find, and the obvious solution. Imagine telling a new programmer "ah, you see, the FilePath doesn't implement the `Display` Trait, so you have to write `println!("(dir) {:?}", path)`, and this is much better than in Go, where you write `fmt.Printf("(dir) %q", path)`". But the author doesn't show the Go solution, because that would reveal that they're essentially quibbling about syntactical choices made in the built-in string formatting library.
Nearly every single complaint is like this. For example, the author seems to be unfamiliar with basic Go naming conventions: "(Can I just point out how hilarious that "Extension" was deemed long enough to abbreviate to "Ext", but "IsPathSeparator" wasn't?)" - yes, because Ext is a much more common thing for calling-code to use, and IsPathSeparator is rare to need. "Familiarity admits brevity" is the saying in the Go community - and as a corollary, the more you use it, the shorter the name can be.
And once you get past the nitpicking of the file stdlib, the author's main complaint is "there's too many dependencies downloaded for some packages", as if they don't believe that the go compiler can do basic dead-code elimination like every other real compiler?
Monotime itself has 0 dependencies, which, again, the author knows, because they pasted the entire source into their article verbatim. If you are stuck on dial-up or something and hate downloading bytes just to throw them away - just copy these two files (12 lines of code) into your project! One of the Go proverbs is literally "A little copying is better than a little dependency."
This isn't a piece with real criticisms, this is a "rant" (self-described by the author) because they're honeymooning with a new, prettier language now (Rust). I find it impossible to believe that the author literally does not know these things after working with Go for "thousands of hours." I'm by no means a Go expert - I haven't written a line of Go for about 2 years, and the biggest project I ever worked on in Go was 499 lines - but the only thing I had to research for this comment was running `cloc` to get those 499 lines.
And indeed, the author started another article about Rust 6 months later, "Learning Rust is... an experience. An emotional journey. I've rarely been more frustrated than in my first few months of trying to learn Rust." When trying to explain how to write a function to add two numbers (the hello-world of functions), they write "We're getting dangerously close to flirting with academic papers at this point, so let's go for an example immediately."
Go isn't Rust. Go is made for building things that the kid 30 days out of coding bootcamp can jump right into and start fixing bugs. If you like Rust, more power to you. But it's not built to be what Go is built to be.
Depends on who reads it I guess. Lispers would not see simplicity in Go, they would see all the different characters you need to use, how some things are statements and others are function calls, conditionals, type definitions and they all have different syntax for themselves, instead of being the same.
Both have their places for sure, but I wouldn't go so far to say Golang is simple because of this example.
Most comments here don't seem to see the context. It's done as companion or reply to the highly celebrated post "A half-hour to learn Rust", which was posted several times at HN and many people got overly excited how easy Rust is.
The takeaway is: The HN community is highly biased. So if you're looking for advice regarding technology choice, take it with a large grain of salt.
Absolutely no offense to the author, but IMO for an experienced programmer the five minute intro does not make any sense. They will be better served by using the documentation (or) go by example in this case.
If someone is new to programming this in no way does any justice. Programming is way too complex to be fit in 5 minutes intro. https://norvig.com/21-days.html
It glosses over a lot of nuance, which I can tell by not explaining that you can't use the implicit assignment := operator outside a function.
And since this has turned into another "referendum on golang" argument, I'll say that I appreciate what it has to offer on my team at work. We historically write in Python, and we are a team of system engineers writing 100-500 line automation code that uses APIs.
Static typing helps. Our enterprise has tooling that makes importing 3rd party code a lot easier in our bureaucracy. The linting and checks are more robust.
From a language perspective, forcing linting and conventions makes the code a lot easier to write with a team. I may not like camelCase, but damnit if everyone doesn't learn how to read it quickly when the language/linter forces us to use it.
The VSCode plugin is completely essential. Being able to type an import, and then quickly hop to the complete API documentation and expected inputs/outputs of everything, is so much help that I, a Python "skilled tradesman", gladly hopped on the golang train.
My only gripe is that a lot of documentation seems to be third party, or very strictly an API reference. Nuanced, helpful docs are scattered in gobyexample, stackoverflow, etc.
---
Bottom line, it is a great language/ecosystem for reasonably self-documenting, readable code for novice/intermediate "scripters" and coders who are not CS majors. Tooling is amazing.
Something it's missing in the CS teaching industry is material (and probably a method too) for experienced programmers who want to learn a new language. It's so hard to find resources to learn the spirit and philosophy behind a language and at the same time learn the syntax without spending time on basic stuff like declaring variables or functions.
I don't think that there is a lack of material, almost every new language has an extensive documentation. I did have to learn how to read something that takes more than a couple days/weekends though, but now I mostly have no problem with documentation.
I do still have trouble with standards or other huge documents, e.g. the Arm Architecture Manual. The kind of documents that are huge, yet assume a lot of previous knowledge so a lot of googling has to be done.
The only thing this missed, and sorely missed was unit testing. Disclaimer: I wrote this post a few years ago and it still seems to help folks, although I'm sure if I wrote it again it may look different. https://blog.alexellis.io/golang-writing-unit-tests/
Go can be understood as an improved C that keeps much of C's simplicity but adds small, powerful features like interfaces and channels and garbage collection
Go puts essential C idioms directly into the language (pointer/length is formalized as slices, packages are part of the language instead of just being naming convention, etc.)
Go is mature, stable, widely-used, well-supported, and well-understood. C++ is a clumsy mess, but C is quite elegant and Go is a continuation/modernization/enhancement of that
Realize a Go []int ("slice of int") is just a C struct like this, passed by value:
struct intSlice {
int* addr;
int len;
int cap;
};
The memory at addr is not owned by the slice. All the slice operations are simply notation for manipulating the struct. Go's garbage collection makes the whole thing work well
This can be confusing if you're used to C++'s std::vector (which owns the memory) or Python's slices. Go's slices are a shallow pointer/length system exactly like is used in C all the time. For example:
void sort(int* addr, int len);
becomes
func sort(a []int)
A Go slice is just a formalization of C's pointer/length idiom, with terse notation for manipulation
Go's slices don't act like what I thought were called "non-owning references"(Rust's &/&mut [T] or C++'s std::span<T/T const>), which reference memory whose lifetime is bounded by something else (like a vector or array or shared pointer). Instead it's "shared ownership" where memory is referenced by one or more slices (and no single owner) which can reference and mutate the slice simultaneously, and it's freed by the GC sometime after all the slices are gone.
This reminds me of Numpy's ndarrays, which act more like "owning containers" (which may sometimes share ownership and alias) than "non-owning references".
Slices are kind of a mess. They should either be uniqueness types or passed by reference (the way maps and channels are, and NIO buffers from Java). Instead they’re passed by value so multiple copies exist whose sizes and sometimes contents aren’t in sync.
Slices should be the absolute bare minimum for a modern systems language, it's not really a reason to switch in my case
If I want to write flat/simple code I'll just C, Go doesn't really offer anything to me because I want to be relatively low-level and able to make the compiler save my time.
Go feels like it's aimed at creating applications that run at a level above where C traditionally runs while keeping the pared down feature set of C, and it just doesn't work well imo.
I loved playing with Go, but eventually it feels like you're conditioning yourself to be ok with a lot of "bad" code. Tons of repetition, tons of boilerplate by other names.
It's like the simplicity gets in the way of itself.
-
My take is, use Go for a bit, take the "less is more" mentality with you to a language more suited for real work.
ps: I'm not saying you can't use Go for real work before someone flies at me. I'm just saying that Go's strength is the mentality people have when using it, as a language it's not particularly enabling, and that's by design if anything...
I explicitly added the "ps" to avoid the situation where all the replies are fairly empty comment saying "Well we use it!"
I get that you can use it for real work, I'm not saying you can't. I'm saying, even by design to some degree, it's not particularly enabling compared to other languages.
That's meant to be a strength if anything, but I did not find the tradeoff of simplicity was able to overcome the additional effort it took to deal with what was missing
There's an implicit ymmv here of course, because I'm not saying no one can find Go useful, I can see how in certain worlds with certain teams with certain priorities it'd all work out.
I have used Go in production. I found it roughly takes 2-3x additional time it takes to develop the same functionality in Node.js (with typescript). But it runs faster. If you want to write code that is probably not going to be thrown away it may be wise to invest the additional time in Go. However can't recommend that for building experimental code/APIs etc which are time bound and may/can get replaced (or split into micro services) in future if it becomes massively successful..
Could be, but it could also be that Node.js have much more libraries compared to Go, especially for databases, can escape typing if necessary using any, promise/async programming are much more easier with es6 (you can do a try catch without repeating etc)..array operations like map, reduce makes much easier to do quick and dirty functional programming etc. I can get much cleaner libraries for concurrent programming in Node.js lots of things you can do in Node.js takes roughly twice or more number of lines. Could be anecdotal, but say if I try another language such as Python I found it is more or less same as Node.js. Java is another language I can think that can slow you down. Of course the stricter typing, error handling etc can make for much more robust code in long run, but for someone who can afford to trade development time.
If I’m willing to take a productivity hit because I can’t afford the footprint of Scala/Java (or Typescript), I’d be thinking Rust. Go seems to fall somewhere in between without a clear advantage in either perf or clarity.
Several use cases, of course more number of npm packages also easier concurrency, much more shorter syntax for async/promises, with ES6 you get closer to functional programming like map reduce etc. Also you can add or escape typing using Typescript. Single threaded architecture is a boon and good fit for web app middlewares. In golang sometimes you have to work too hard to get typings correct, concurrency is not straight forward in my view and error handling requires lot of repetitive code etc and of course lesser number of packages and difficult to hire developers.
Absolutely! There are obvious productivity differences between the languages themselves and frameworks can make it even more apparent.
Different languages make writing the DSLs used in Rails-like frameworks easy, difficult or impossible. Much of my professional career was spent working on Node back-ends with a variety of frameworks and none of them were even close in terms of succinctness or productivity.
> I loved playing with Go, but eventually it feels like you're conditioning yourself to be ok with a lot of "bad" code. Tons of repetition, tons of boilerplate by other names.
Not well or hastily written Go is easily like that. I can only guess why you point out the repetition but when you look at well written Go code like in the Go std library, there isn't much repetition happening and it looks rather elegant. At the same time it can be quite some effort to create such code and might take several iterations. Documentation-wise I like the official documentation, it gives a lot of pointers why Go is how it is.
Also obviously it's not for every use-case. Where it works really well IMHO is where error handling is a vital part of the application. (And of course anything concurrent is quite a breeze)
Not having generics, by definition, invites a lot of repetition and boiler plate. I know generics are overrated and coming soon, but right off the bat that stuck out.
I recall a point where I had written a function that needed to return a channel, but of course had to forgo types due to the lack of generics.
The end result was having to wrap that channel in another channel that added typing at each call site.
I asked around in go circles if that made sense, and called out how bad the felt but the answer was "no that's great! channels are cheap! the repetition is good because it's simple!"
-
The error handling has some sore points to that end too, and the implicit shadowing with shorthand assignments on one hand makes it easier to deal with multiple errors, but on the other hand introduced subtle logic bugs on one than more occasion where "err" was silently shadowed, which I greatly disliked.
I'm actually suprised Go didn't forgo shadowing for the shorthand operator and force people to label their errors when dealing with multiple, it seems very "in brand", but I guess even Go draws the line somewhere lol
-
Then there's the whole stack trace situation. Which after plenty of reading still just wasn't making sense. I mean the idea of logging a stack of wrapped messages sounds very nice, but man proper stack traces not being the first class citizen of error handling just did not make sense to me in a language I hear referenced for systems work so much (and I realize they can be had)
I saw proposals to rework Go's error handling, I don't know if any progress has been made to that end, but it's another example where simplicity can get in the way of itself
-
And I'll balance this all out by saying it was still fun to write, I don't want to seem like I'm just shitting on Go for existing.
It's just these little warts that I kept getting over with just a little bit of "idiomatic Go" which I often found was just writing a little bit more code than you're used to, started to add up.
And eventually I realized I was creating something that, while very easy to reason about, had a lot more to reason about than it needed to. That's where I kind of petered out in my personal usage.
It probably doesn't work to convince you, still I see that differently ;)
About generics, it depends on the problem at hand. When writing a generic Matrix multiplication type that for instance should work both on real and complex numbers (or more obscure types) generics are a thing. On the other hand, when working for instance with golang.org/x/net/html, I really appreciate the interface definitions and the possibility to do type switches on those.
The error handling problem can also be dealt with. I also prefer to not shadow variables, IMHO the ideal function has all its variables defined already in the signature.
I mean there are stack traces when a panic is called, or something that wraps it like a log.Fatalf. I guess the idea is the author is giving much more thought to how errors are dealt with and how they are printed in a both useful (=greppable) and beautiful way. As an end user that doesn't want to dive into the code I probably always prefer to see a nicely formatted error message (or even a gracefully handled error) instead of a 100 line stack trace. Probably Go is not just the language spec but also how to use it. Therefore saying "Learning Go in 5 minutes" is probably a bit far-fetched ;-) (Probably for any language that would be true...)
I'm aware of stack traces on panics, but my understanding is you only get them if you let them bubble up and kill the application (so not really a "first class" option for errors in places like web handlers) Has that changed, or did I misunderstand?
But I also think the stack trace message example is the perfect example of what I mean by "take the Go mentality with you"
In a language like Kotlin for example, Go got my in the habit of attaching more information to exceptions as they bubble up.
I was already generally in the habit of wrapping library exceptions, but I started religiously using a Result nomad implementation and wrapping errors with additional information so that my stack traces looked very "Go-like" in terms of human readability, but kept traditional stack trace information.
It's small things that I think using Go makes you "re-appreciate" in other languages
You can get the full stack of the crash point (and a few extra frames that handles the stack generation) in a deferred recover, ie https://play.golang.org/p/XvZteY6Y6fh
github.com/pkg/errors is a simple way to dump expensive stacktraces into each new/wrapped error. Or you can just panic and recover in your own app, it's possible though discouraged.
Sometimes I don't know why I bother talking about Go.
It's proponents are so quick to go defensive that no one is capable of reading any complaint in an even mildly charitable light.
I mean I shouldn't literally have to say "I'm not saying you can't use Go for real work before someone flies at me." When talking about a language as widely used as Go right? Like that's common sense!
And yet of course I'm getting replies that do exactly that because of how darn defensive people need to be about it.
There is a literal full paragraph dedicated to explaining it, maybe don't ignore the oodles of context I left to avoid exactly what you did.
Saying other languages are more suited to real work doesn't mean Go isn't suited at all, it literally means other languages are more angled towards the "end product" or the "real meat and potatoes" of just making a thing work than Go.
It's literally a strength of Go. Go doesn't want to include the kitchen sink or even confine you to having a kitchen at all and I respect that.
But I'm saying that other languages that include more in the way of affordances can help one be more productive, and I believe combining a language that includes more, with the mindset of not abusing the buffet is a winning combination.
Ymmv, I'm not an oracle, I just expect people to read things in a reasonably charitable way which usually works fine as long as they're not being defensive.
I will take sometimes-tedious boilerplate over many-many-layered abstractions.
You can write any language any which way, but some languages just have a culture of boilerplate vs abstractions. The most popular Python and Java libraries seem to love endless layers of indirection, and when it doesn't fit exactly what you need, you bang your head against the wall monkeypatching it to make it work since your whole app is already written in X mega library.
Go culture seems to be quite a bit more boilerplate-y and uses smaller more composable libraries. It's just refreshing.
Really I just need to buckle down on Rust, but I think it's a fair bet that teams and shops that bring in a lot of young programmers and want to ramp them up fast may cringe a bit at the learning curve of Rust.
That's why for now in terms of a bet on employability, I am prioritizing experience with Go.
I agree about becoming okay with bad code. But I think if you understand and embrace that, there’s a place for it and it can be quite powerful and easy to use at times. It’s a trade off that makes sense sometimes. I think the problem appears when people treat Go like it doesn’t have any problems. All languages do.
As I’ve mentioned recently though, I don’t think I’d ever want to use Go as a forever solution to a complex problem. I’m sure some people could do it and do a great job, but I’m not one of those people. I need the ability to abstract and ensure more safety, or I’ll never feel secure with what I’m building.
I like C but I would prefer Rust over Go. Go may be simple but I really hate using conventions like init(), comments working like syntax, captial letter means published etc.
Doesn’t Rust have “comments working like syntax” in the form of #[derive] etc.?
I’ve only read through the rust book without using it for anything (yet) and have done no work on golang, so I’m legitimately curious to hear your input, not challenging you on semantics :p
Edit: Rust does not use # for comments. Sorry I just had that mixed up in my head, but thanks for the answers!
These #[...] and #![...] are not comments; they're attributes (somewhat similar to Java's @Something annotations). Comments in Rust start with // or /<star> like in C or C++. There's only the special case that "doc comments" (starting with /// or /<star><star> or //! or /<star>!) are automatically converted into #[doc="..."] or #![doc="..."] attributes.
The difference is that attributes are always meaningful to the compiler (and can only be used in specific places); other than doc comments (which turn into attributes), comments never are meaningful to the compiler.
(edit: HN's comment markup eats the * character, I replaced them with <star> above)
init() is not a convention and it's not used that much. For comments I don't understand what you're saying. And for caps I think it's better than public / private keyword.
Certain comments have a special meaning to some tooling. In this sense, it's not very different from Javadocs.
For some examples:
A comment that appears right before a type or function definition is exported as documentation by the "go doc" tool.
Another one is build constraints, which appear as a comment at the top of the file, like: `// +build linux` to say "only include this file if the build target is linux."
Other tools use their own comment format to recognize comments meant for them.
The Go authors have (ab)used comments to extend the language in experimental ways for code generation, build pragmas and recently file embedding.
They’ve done this to avoid breaking the language stability guarantee, not sure if there were other reasons. I think it’s a mistake personally to create this sort of metalanguage in comments (now you have two problems!) but it’s easy to avoid using this most of the time as the uses are pretty esoteric.
I just keep my templates and config in separate files.
There are good reasons to keep them separate anyway (allows config without recompile for example) and for the apps I work on distribution isn’t a problem. So I haven’t really had to use comments as code in 7-8 years of go. Perhaps it’ll become more common with the embed stuff. But then I don’t use struct tags either so I guess I’d prefer Go to be even simpler.
No, Go comments are just comments. They have been used though to add meta-information to special tools. So unless you use those tools and require to add the specific meta-information, you can treat comments as plain comments.
Hiding certain meta-information, like exporting a function with a C-compatible signature when building a shared library is not a great way of doing things, but on the other side, it is a special usage which isn't part of the language itself, so not part of its syntax.
My main small day-to-day usability gripes with C are lack of type inference, lack of destructors and terrible ergonomics for error handling. Go solves two out of three, but it really dropped the ball hard on error handling which is very disappointing IMO.
Things like generics and garbage collection involve some deep tradeoffs, so I get Go's take on them, even if I don't necessarily agree with it. On the other hand IMHO Go's crappy error handling boilerplate is not something I find acceptable in a modern programming language.
I agree that the error handling is terrible, but I'm not sure how much better it could be without generics.
For me, the gold standard in error handling is Rust, where the type system simply does not allow you to ignore errors. This means that the existence of error handling is checked at compile time. Of course, this doesn't mean your error handling is _good_, but I think that's beyond any language design to enforce.
But the Rust system is entirely built around a generic Result type, where both the non-error result (an int, string, struct, etc.) _and_ the error type itself are both generic.
Go uses compiler magic to have effectively generic collections, why not hardcode a Result type in the same fashion? It's not very elegant, but it's pragmatic and error handling is important enough that it may well warrant it.
I've been talking to the local Go user group. No one even knows about that concept. This continues a pattern of general ignorance/lack of looking beyond one's horizon I've noticed.
The error handling feels deeply wrong to me too. I love the explicitness and how hard it is to ignore errors - all languages should make it so easy to expect and handle potential errors - but other features (or lack thereof) of the language make it so you can’t handle any of it in concise or sane ways. Code is plastered with the same boilerplate, everywhere, always. It makes up a significant mount of all Go code.
I'm not a C fan but isn't Zig much closer to C's philosophy
while fixing bad parts than Go? Especially due to the garbage
collection, at which point Go suddenly competes with many more
simple languages.
It depends on your definition of "C's philosophy", no sarcasm, and no implied preference by me for any particular definition, as I believe many have merit. Language philosophies evolve, and C, being so many decades old, has evolved a lot. The original philosophy is probably little more than a seed crystal at this point for the several philosophies that have been loaded on to it over the years.
It is probably reasonable to say that Zig is closer to C's current philosophy in practice, which encompasses many additional developments over the decades to deal with various aspects of C and programming real machines that have arisen as both have changes, and Go closer to C's original philosophy of simplicity and relative ease of use. (In modern times, C is not one of the easier languages to use any more, due to significant advancements in the field of making easy-to-use languages. But it was originally intended to be an advancement there itself.)
By similar logic, I can say with a straight face that all of Go, C++, Zig, Rust, and Swift (and more) are "evolutions in general-purpose programming languages like C", despite their wild differences, because the world has gotten larger, and there is room for a lot of variations like that, each addressing different aspects of the newer, larger world.
As Rob Pike writes in that link, they could've fixed it and still had declaration resembling use if they had chosen a suffix operator for pointers. He notes that Pascal uses ^, but it seems like @ might be a better choice:
int foo // an integer
int foo@ // pointer to int
int foo[10]@ // 10 pointers
int foo@[10] // pointer to array
int foo()[10]@ // function that returns an array of 10 pointers
In my opinion, they almost fixed it and then punted. As such, it's just different, but not really better.
To me Rust is more like "C++ done right". It's massively bigger, more complicated and slower to compile than C. The C ABI is also the de-facto lingua franca for language interop, something that's not easily achievable with Rust or C++ (or Go, for that matter).
I'd like to chime in and say that I also like C but really dislike Go.
C is simple, to what today is nearly an absurd degree, but it at least integrates well with an enormous ecosystem of existing tooling and libraries. Go, by contrast, feels the need to re-invent every possible wheel, often with a seemingly intentional effort to be different just because.
C ABIs are rock solid on every platform. Any language or tooling in the world can load functions from a C library. By contrast, try calling Go from another language. You will quickly give up and use network protocols or subprocesses instead.
The converse is true too: on major platforms, the C library defines the operating system more than the kernel does. (I mean, in a Unix sytem, the entire libc is documented in the man pages. Documentation for writing C is literally built into the operating system.) By contrast, Go decides to avoid the standard platform ways of calling every operating system function, and instead implements syscalls directly into the kernel, basically just because Go hates interoperating with anything that's not Go.
C is a good language not because it is a good language, but because it is a good ecosystem. Unix and C are closely intertwined. (Even Win32 and C are pretty closely intertwined!) I could imagine a Go-based Plan9-like system -- if that existed, and had decades of engineering behind it, I could see the argument being made that Go's ecosystem compares to C's. But while Go may aspire to that, it isn't anywhere near there yet.
And when you start comparing Go with other languages on the basis of language features, I think Go fails laughably more often than not. (I'll give you "C++ is a clumsy mess", though.) But compare Go with Rust, with C#, with Haskell -- for that matter, compare Go with Java and I think Go is a less powerful, less useful language to write real code in.
If I want a lightweight, quick-and-dirty program, I'll use Python. If I want to write a reusable library that gets plugged into other software, I'd go with C (or at least write the interop layer in C). If I want a high-performance server, these days I would probably go with Rust. If I want a pragmatic, well-rounded language to write a large project in, I think C# would probably be my first choice.
What space does that leave for Go? Outside of microservices (where I contend that Rust is a much better choice, and there are lots of other options), Go's niche seems to be command-line tools. I sort of understand that, because Go's static linking makes redistributing binaries relatively easy (although Go by no means has a monopoly on that). But in the space of command-line tools, Go's argument handling is frankly bizarre: it looks at decades of precedent about what users expect from argument syntax and behavior and decides to throw it all away and reinvent the wheel.
I guess that means this comment (rant?) has ended up where it started: Go defies convention, basically "just because". By contrast, C defines convention. "If you like C, you will love Go"? Nope, not at all.
Disclaimer: I wrote mostly Java for 10 years, and Go for about 5.
Go straddles an interesting space. Currently I work on a back-end data crunching software, distributed (running on hundreds or sometimes thousands of nodes), each node running 64 or 128 cores with 128 or 256GB RAM. The problem we solve is both IO, CPU, and memory bound. Anyway to look at it, it's a hard problem.
The old version of this program was written in C. Then it was rewritten in Go.
The people who decided to switch to Go was looking for the following features:
- Be able to manually lay out memory for performance. So not Java.
- Mmap binding to file system, for performance.
- More abstractions than C, for adding software features.
- Cross platform. The program sometimes runs on Android and IOs.
- Easy to learn for beginners. It's not possible to only hire PhDs or C++ gurus. - Easy to dig down into assembly, for performance.
- GC doesn't get in the way, for performance as well as adding features.
Maybe these days, C# might work? May Rust? But 6-8 years ago, Go fit the bill and it worked out very well. And the language doesn't feel "limited" in any way. Yeah, I miss generics sometimes. Maybe error handling could be better? Sometimes I need to work around the GC. But day-to-day, I think I focus more on solving actual problems (performance bottlenecks, adding features), instead of pondering if I need to create another AbstractFactory, or tuning GC knobs, or figuring out which pointer type to use.
To me, Go feels very balanced. I can write python or Java like code and get decent performance. If I need more performance, I can dig down pretty deep make CPU work faster. The abstractions are light enough that a new person can figure out what's going on and add features, fix bugs, or improve performance.
Learning Elixir was tough for me until I found Dave Thomas' "Elixir for Programmers" where he skipped all of the basic stuff that every programmer is assumed to know and went right to what makes Elixir unique and how to leverage it correctly. (https://codestool.coding-gnome.com/courses/elixir-for-progra...)
I have been hunting for the same thing for the Go language for years because frankly I haven't been able to grok the appeal. I am hoping to find a 'Go for Programmers' one day. I asked on Reddit a while back and was pretty much handed the "what are variables" document.