9 ms·
But no one said Go was easy. It's just simple and easy to reason about. There's a huge benefit to something doing exactly what it says. No hidden behaviours, no
by Philip-J-Fry 6y ago
But no one said Go was easy. It's just simple and easy to reason about. There's a huge benefit to something doing exactly what it says. No hidden behaviours, no overly complex syntax, nothing like that.
I came from C# where I have gotten increasingly frustrated with the amount of additions to the language. Like we now have classes, structs and records. We now have async/await but we also have channels. This is what having a huge toolbox causes. Some people are going to write code using channels, me others will use async. Some people will use features that others don't care about.
I think there's huge benefit in a simple language. In Go I know that there's only 2 ways to do concurrency. You either use go routines and mutexes or you use go routines and channels.
Generics will bring a lot of helper functions that weren't possible before which will remove a lot of repetitive code.
But otherwise I am super happy with writing Go code. All my pet peeves of something like modern Java or C# are gone.
- thayne 6y ago> But no one said Go was easy. Lot's of people have said go is easy. In particular that go code is "easy to read" is an oft-cited benefit of go. Also, if you go to golang.org and the first thing you see is: > Go is an open source programming language that makes it easy to build simple, reliable, and efficient software. (emphasis mine)
- Philip-J-Fry 6y agoGo is easy != easy to read. I personally do think Go is easy to read. The fact that I don't get bitten in the ass by hidden behaviours and there's no inheritance to step through 5 files of. The fact that everyone's Go code looks more or less the same (unless you're an absolute beginnner) because there's only so many ways to do something. Compared to C# where reading someone else's code means I have to take a shot to calm my nerves and then take a guess at which permutation of language features they decided to use today. A simple language is easier to read. This doesn't mean it's going to be easy for anyone who isn't a Go developer. There's no claims of that. But given a week to understand syntax and getting used to reading files, I don't think there's many places where you'd get stumped. >Go is an open source programming language that makes it easy to build simple, reliable, and efficient software. This still holds true for me. A simple language builds simple software. No noob Go dev is going to have a good time, but that's true in almost every language. Once you understand how everything works, how interfaces work, how channels work, you can simplify down most problems and build them with ease. Needing to write a few extra lines of code doesn't mean you can't write simple, reliable and efficient software. Neither of those quotes say Go is an all round easy language. It's not like you can throw it at a baby and get back Kubernetes. It's not easy to do custom containers, it's not easy to do manipulate lists, it's not easy to do a bunch of things you'd do in one line in something like C#. But I think these are small hurdles for the mental burden you're relieved of when you see that the rest of the language is equally as simple and benefits from it.
- rswskg 6y agoI think c# is an unfair comparison in this instance. You are right, c# does too much. But Go makes it far more work to do basic things - list comprehension for example (what is everyones obsession with for loops?). An opinated language with one way to do things sounds great, but not at the expense of basic niceities you get in other languages.
- nmfisher 6y agoFor what it's worth, I'll take for loops over list comprehensions any day of the week. I just find it a lot easier to read.
- random5634 6y agoAgreed! Check out pythons move with walrus operators inside list comprehensions - I think u can do that now - starts to be line noise
- simiones 6y agoSo you are claiming this arr2 := []int{} for i := range arr1 { if arr1[i] > 9 { arr2 = append(arr2, arr1[i] * 2) } } Is more readable than these? var arr2 = arr1.Where(x => x > 9).Select(x => x*2); var arr2 = from x in arr1 where x > 9 select x*2; arr2 = [x * 2 for x in arr1 where x > 9] (setf arr2 (loop for x across arr1 when (> x 9) collect (* x 2))) I honestly can't imagine by what measure any of the latter ones could be harder to understand the former. And as the complexity of the expression increases, I only see the advantage increasing typically (though often it pays to split it into multiple comprehensions, just like a loop that does too much).
- nmfisher 6y agoPersonally? Yes (except for the first example you gave, which I don’t classify as “list comprehension”). It’s not inherent complexity though, just personal preference. I grew up writing for loops so they’re second nature to me and I grok them instantly in a single pass, whereas some list comprehensions require a re-read (particularly if they’re nested). Different strokes, that’s all.
- nvarsj 6y agoGo _is_ easy to read. I can read almost any go codebase, even giant ones, and see what's going on pretty quickly. Same with C - the Linux kernel is surprisingly easy to understand once you know the basic data structures it uses. There is a lot of benefit in using simple/limited languages from a readability point of view.
- owl57 6y agoThe data structures in C require thorough documentation to have any chance to keep your foots. And keeping the preprocessor wizards in check. But I agree that Linux authors have done a consistently good job. In fact, a kernel is even easier to read than other programs whose authors worked as hard, because having no external libraries at all also helps.
- johannes1234321 6y agoReading the kernel is relatively easy since some people worked hard on a structure, which is relatively clean. There are many macro C code bases with tons of function pointer fun (for example when doing OOP) where you have a hard time to find things, till you spent considerable time learning the choices made in that code base.
- nvarsj 6y agoYeah true, macro heavy code can be really difficult, as you can make your own metalanguage with macros. The ruby codebase is a lot like that, but I still find it pretty easy to read. But I can imagine someone going crazy with macros could really make it hard, but that's not the norm in my experience.
- toolz 6y agoMaybe for small pieces of code, but getting the overall higher level feature goals of code in go is hell (for me). The lack of function overloading to generalize intent for generic work is abysmal and outdated. It pains me to no end that their built in functions are overloaded, but I can't do it in my own go code. Generally speaking if you're modestly familiar with a language it's rare that it's difficult to read small pieces of code. The hard part of programming on a team is writing code such that high level intent is quickly apparent with APIs that support that intent and make it easy to understand where edge-case handling is happening vs direct feature intent
- baby 6y agoGo is easy to read
- fomine3 6y agosimple != easy.
- threatofrain 6y agoBut for most programmers, easy outweighs simple in the hierarchy of values.
- felipellrocha 6y agoWhich is a problem. (Though, i will say i think go is neither easy nor simple, but i believe that is an unpopular opinion here at HN)
- lima 6y agoSimple > easy should be common sense for anyone who ever worked in a team. Code is written once and read many times (and usually not by the author).
- deleted 6y ago[deleted]
- rednum 6y agoActually Rob Pike said that Go was supposed to be "easy to understand and adopt": https://www.youtube.com/watch?v=uwajp0g-bY4 https://www.youtube.com/watch?v=uwajp0g-bY4
- BenFrantzDale 6y agoThis is the same guy who somehow thought a half-speed language without generics was a somehow going to be a C++ competitor.
- simiones 6y agoOf course Go has hidden behaviors and overly complex syntax, like any programming language before or after. For example: xrefs := []*struct{field1 int}{} for i := range longArrayName { longArrayName[i].field = value xrefs = append(xrefs, &longArrayName[i]) } Perfectly fine code. Now after a simple refactor: xrefs := []*struct{field1 int}{} for _,x := range longArrayName { x.field = value xrefs = append(xrefs, &x) } Much better looking! But also completely wrong now, unfortunately. `defer` is also likely to cause similar problems with refactoring, given its scope-breaking function border. And Go also has several ways of doing most things. Sure, C# has more, but as long as there is more than one the problems are similar. And I'm sure in time Go will develop more ways of doing things, because the advantage of having a clean way to put your intent into code usually trumps the disadvantage of having to learn another abstraction. This is still part of Go's philosophy, even though Go's designers have valued it slightly less than others. If it weren't, they wouldn't have added Channels to the language, as they are trivial to implement using mutexes, which are strictly more powerful.
- feffe 6y agoI don't think this is a good example of proving the point that Go is difficult to read. Isn't it expected to internalize the semantics of basic language primitives when learning a new language, or does people just jump in guessing what different language constructs do? IMHO you learn this the first week when reading up on the language features. range returns index and elements by value. The last example does what you asks of it, it's like complaining something is not returned by reference when it's not. Your mistake. Perhaps some linter could give warnings for it.
- simiones 6y agoI don't think you can truly internalize the varying semantics of which operations return lvalues and which return rvalues. At least, I haven't yet been able to in ~2 years of Go programming, and it's a constant source of bugs when it comes up. The lack of any kind of syntactic difference between vastly different semantic operations is at the very least a major impediment to readability. After all, lvalues vs rvalues are one of the biggest complexities of C's semantics, and they have been transplanted as-is into Go. As a much more minor gripe, I'd also argue that the choice of making the iteration variable a copy of the array/slice value instead of a reference to it is the least useful choice. I expect it has been done because of the choice to make map access be an rvalue unlike array access which is an lvalue, which in turn would have given different semantics to slice iteration vs map iteration. Why they chose to have different semantics for map access vs array access, but to have the same semantics for map iteration vs array iteration is also a question I have no answer to.
- callmeal 6y ago>This is what having a huge toolbox causes. Some people are going to write code using channels, me others will use async. I don't know if this is a problem. Redundancy is good. If simple languages ruled, we would all be speaking Esperanto. >Some people will use features that others don't care about. I end up having this argument with product managers who insist that because most people only use 60% of the functionality of a product, we don't need to implement the remaining 40%. You need more than one way of doing the same thing.
- sagichmal 6y agoOn the contrary, every instance where there is more than one way to do something is a failure of the language. It's OK, all languages have failures, but the ideal is pretty clearly a set of precisely orthogonal features, where there is neither repetition nor exception.
- dTal 6y ago> It's just simple and easy to reason about. These aren't the same thing. Brainfuck is "simple" in the same sense - no hidden behaviors or complex syntax - but it's virtually impossible to tell at a glance what a Brainfuck program does, or say with certainty how it will react to different inputs. Complexity has to live somewhere, and the more restrictive the language, the more complexity is moved to the program structure. Conversely, every special-purpose construct in a language is a place where you don't have to rely on reasoning about an unboundedly complex Turing-complete system, and can instead go and check the documentation for what it does.
- corty 6y ago> No hidden behaviours, no overly complex syntax, nothing like that. Arrays vs. slices and standard functions dealing with slices are full of weird behaviour, unnecessarily complex syntax and obscure features. Like, why are there even arrays at all? Which slice functions return a copy and which ones don't? Why is append() so braindamaged to make a copy sometimes? Why do I need make() as often as I do when mostly the language knows all make() would do? Go still has lots of footguns, even right at the trivial basics.
- deleted 6y ago[deleted]
- jerf 6y ago"Like, why are there even arrays at all?" Because while Go is garbage collected, it also provides modest control over memory layout. Arrays are allocated chunks of contiguous memory for holding a fixed number of things. Slices are something that sit on top of that and let you not worry too much about that. You almost never want arrays yourself, but they are a legitimate use case that the language has to provide because you can't create them within the language itself. But the right answer for most programmers is to ignore "arrays" in Go entirely. "Why is append() so braindamaged to make a copy sometimes?" (Tone: Straight, not snark.) If you understand what slices are and how they are related to arrays, it becomes clear that "append" would be braindamaged if it didn't sometimes make copies. Go is simple, sure, but it was never a goal of Go to try to make it so you don't have to understand how it works to use it properly. It is a fair point that quite a few tutorials don't make it clear what that relationship is. Most of them try, but probably don't do a good enough job. I think more of them should show what a slice is on the inside [1]; it tends to make it clear in a way that a whole lot of prose can't. "Show my your code and I will remain confused; show me your data structures and I won't need to see your code; it'll be obvious." [1]: https://golang.org/pkg/reflect/#SliceHeader https://golang.org/pkg/reflect/#SliceHeader
- DennisP 6y agoI don't entirely disagree but I can't help noticing, you mentioned two ways to do concurrency in C# and two ways to do it in Go.
- mbesto 6y ago> But otherwise I am super happy with writing Go code. All my pet peeves of something like modern Java or C# are gone. I'm always confused by this. Why do people consistency cherry pick specific languages to compare then when those languages offer fundamentally different paradigms? I understand comparing C/C++ vs Go, but isn't obvious that C# is going to be fundamentally different than Go?
- Agingcoder 6y agoYes it's obvious, but it doesn't prevent OP from liking go more than c#/java. Furthermore, its very likely that the languages people 'cherry pick' are actually languages they use everyday. So they just compare 'new everyday language' to 'previous everyday languages', and just tell you whether they're happier or not when they write code. The point is not really about the language theoretical properties and merits, but how people experience the language in real life.
- simiones 6y agoC# and Java are much closer to Go than C++. C is also closer from another perspective (simplistic syntax). C++ is about as far from Go as you can imagine a language. Maybe only Prolog would be a worse comparison point. C++ is designed with one goal in mind: no special compiler built-ins. If it is possible to do it at all, it can be done in a 3rd party library. C++'s design philosophy is Library first. Bjarne Stroustroup explicitly advocates this: don't write special case code. Write a library that solves the problem, then use that library to achieve your special case. Go doesn't think writing libraries is a useful endeavor for most programmers: they should be writing application code, and let their betters design the tools they need, build them into the compiler, and stop wasting time designing your own abstractions.
- mbesto 6y ago> C++ is about as far from Go as you can imagine a language. Maybe only Prolog would be a worse comparison point. https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html https://commandcenter.blogspot.com/2012/06/less-is-exponenti... /headscratch EDIT: I think you're misinterpreting syntax of the language with use case. e.g. low level vs high level programming language
- Hawxy 6y ago> Like we now have classes, structs and records. IMO records are a great addition, especially when it comes to point-in-time/immutable data. They also provide an improvement to developer efficiency, as basic data classes can be represented with a single line record definition. > We now have async/await but we also have channels. Some people are going to write code using channels, me others will use async. I'm not really sure what you're talking about here. C# has no concept of a channel. If you referring to the "System.Threading.Channels" package, it exists for those in the community that would benefit from it, and still uses familiar async/await syntax. It's also a very niche package that is unlikely to have significant adoption, so there's no real concern of the pattern "segmenting" the community.
- kenniskrag 6y agoWe solve that with communication to use a to the team well known subset of the language and internal trainings if we see potential helpful features.
- sigzero 6y agoI am pretty sure I have read something similar to "Go is easy" a multitude of times.