13 ms·
One benefit of radical go-style simplicity that I haven't seen discussed much is that it forces you to focus on the task at hand. Like, you know that your code
by dunkelheit 7y ago
One benefit of radical go-style simplicity that I haven't seen discussed much is that it forces you to focus on the task at hand. Like, you know that your code will look like shit anyway so it is pointless to fuss too much over it. Whereas many programmers using a more "clever" language like haskell will spend a lot of time trying to make code more readable, searching for the right abstraction, arguing whether something is a monad or some other gizmo. Most of it is wasted intellectual effort.
Everything in moderation of course. For me personally simplicity of go is too much and I don't feel comfortable writing it.
- RHSeeger 7y ago> Most of it is wasted intellectual effort. I would challenge this. I would say _some_ of it is wasted, but most of it works towards making the code more understandable. And code being understandable to the next person to read it (both in the small "what is this block doing" sense and in the larger "what is this algorithm doing" sense) is very important.
- sidlls 7y agoI can't speak for the GP, but my interpretation is that the wasted effort comes from doing what you describe when, instead, one could have chosen a simpler language and produced understandable (albeit "ugly", "not eloquent") code the first time round. Then one can turn those intellectual wheels on a more interesting problem to solve.
- chc 7y agoThe assumption that simplicity in a language naturally encourages understandable programs is a mistake IMO. Language complexity generally exists because the language is trying to shoulder some of the complexity that would otherwise go into your program. For example, Brainfuck is nearly the simplest language possible — you could write a full rundown of its features on a sticky note — but programs written in it are not very readable. Even Go did this by adding async features into the core language. This is a complication that doesn't exist in the older languages it was intended to build on, but by building that complexity into the language, they reduced the burden of using it in your code.
- dunkelheit 7y agoSome of it is wasted, some is genuinely useful and to know which is which you need experience and good judgment. My point is that it is incredibly easy to fall into the trap of pursuing maximum code beauty and abstraction and wasting a lot of time in trying to attain some shining ideal, especially if the language is conducive to it.
- tfha 7y agoI find other people's go code is generally much more readable than other people's code in most other languages. Go's simplicity and heavy idiomatic culture means all the code more or less looks identical. This is great for team projects.
- frou_dh 7y agoIs the much touted "Make invalid states unrepresentable" consequence of garden-variety Sum Types wasted effort? Seems very good bang-for-mental-buck to me.
- rectang 7y agoJoe Doffy argued, in my view persuasively, that Go missed an crucial opportunity by not requiring that users actually do something with returned error codes. http://joeduffyblog.com/2016/02/07/the-error-model/#forgetting-to-check-them http://joeduffyblog.com/2016/02/07/the-error-model/#forgetti... > It’s surprising to me that Go made unused imports an error, and yet missed this far more critical one. So close! Result/Option/Maybe types force unwrapping, which makes ignored return codes auditable and allows you to manage technical debt. This doesn't speak to Go's simplicity so much as it does to Go's conservatism. Having the Go standard library use sum types, establishing a precedent and a culture, would be no more complex in the absolute, but would have been more of a stretch for its initial target user base.
- masklinn 7y ago> It’s surprising to me that Go made unused imports an error, and yet missed this far more critical one. So close! The most egregious to me has always been that unused imports are an error but variable shadowing is not. Even in languages with dynamic side-effecting imports (like Ruby or Python) I've never seen a bug caused by an unused import. Not so for shadowing (don't get me wrong, it's a convenient feature, but if you're going to remove this sort of things because reasons it's a much bigger pitfall than unused imports).
- kemitche 7y agoI agree with you. And it would even be fine to allow shadowing but require an explicit declaration to allow it to happen in a particular case. e.g. import "foo" func other() { shadow var foo string = "bar"; }
- 7y ago
- corporateslave5 7y agoSome of us need the intellectual delight to make the work bearable. If I can view my code as some kind of art, constructing great abstractions, it helps me forget that I’m spending a huge portion of my life logging, aggregating, and analyzing internet clicks.
- sagichmal 7y agoYou should view this condition as something to fix in yourself. Take joy in doing these things with precision and efficiency, and making your work easy to understand and explain to others. It's hubris and, frankly, rude to subject your professional colleagues to your artistic expression.
- ahelwer 7y agoLook bud, not all of us want to take delight in being an easily replaceable automata. It ain't a personality flaw.
- derefr 7y agoSo you’d take joy in making your coworkers’ lives hell instead? I understand the kind of joy-of-expression that lives in things like https://poignant.guide/dwemthy/ https://poignant.guide/dwemthy/, but if you attempt to put that in a production codebase, I’m not gonna be at-all positive in code review.
- ahelwer 7y agoGreat, I'll let every reviewer of my code know that they've been doing it wrong.
- derefr 7y agoAre you saying that you actually write the kind of code that I linked? Because my expectation was that you’d look at it and say “well, my code certainly doesn’t look like that. It’s quite reasonable in comparison, in fact.” I don’t think I’ve ever seen anyone write code like Dwemthy’s Array in a project that has to “do something productive”, even if they’re the only one working on it.
- matthewmacleod 7y agoI find the opposite is true. It too often means the focus is on fiddly and tedious book-keeping - instead of writing some code that says “please do a thing”, I write some code that says “please perform these 20 steps to do a thing in excruciating detail even though you are much better at deciding how to do this than I am”. It’s noise that detracts from the readability of the code far too often for my taste.
- zer01 7y agoThis has absolutely been my experience as well. Golang is good at being fast, but to say that it helps write better code because of its missing batteries/features is just silly I think. "I want to interact with a REST API and pull a field out of its response JSON" is an incredibly common workflow, and yet to do that in golang is far from trivial. You need to define serializer types and all sorts of stuff (or you can take a route I've seen encouraged where people to use empty interfaces, which can cause runtime exceptions). Same deal with a worker pool. Concurrency is great, but instead of providing a robust, well written solution as part of the language itself, it gives you a toy like this https://gobyexample.com/worker-pools https://gobyexample.com/worker-pools (still the most common result on Google) that is only 80% of the way there. Then you find yourself bolting things onto it to cover your features (we need to know if things fail, so let's just add another channel. We also need finality, whelp, another channel it is), and before you know if you have an incomprehensible mess.
- sagichmal 7y ago> "I want to interact with a REST API and pull a field out of its response JSON" is an incredibly common workflow, and yet to do that in golang is far from trivial // Interact with a REST API and pull a field out of its response JSON. func interact(url string) (field string, err error) { resp, err := http.Get(url) if err != nil { return "", fmt.Errorf("error making HTTP request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("error querying API: %d %s", resp.StatusCode, resp.Status) } var response struct { Field string `json:"the_field"` } if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { return "", fmt.Errorf("error parsing API response: %w", err) } return response.Field, nil } IMO this is a good level of abstraction for a language's standard library. Each of the concrete steps required for the process are simply and adequately represented. Each can fail, and that failure is idiomatically managed directly and inline, which, when applied to an entire program, significantly improves reliability. If you find yourself doing this often, you can easily write a function to do the grunt work. Probably func getJSON(url string, response interface{}) error > Same deal with a worker pool. Concurrency is great, but instead of providing a robust, well written solution as part of the language itself, it gives you a toy like this Go's concurrency primitives are very low level. This can be bad, but it can also be good: not all worker pools, for example, need the same set of features.
- atombender 7y agoI agree that Go forces you to focus on mechanics of your code more so than some "fancier" languages. However, Go's poor type system also forces you to write worse and less safe code than you want. For example, the lack of enums and sum types means it's hard to represent, safely and efficiently, anything that should be the same type but have different values within a strict set of allowed values. The nearest you get in Go to an enum is: type Color int const ( Red Color = iota Blue Green ) Alternatively, can do the same with a string: type Color string const Red Color = "red" // etc. This gives you some type safety, since a Color requires casting to force an int or string or whatever to become it. But that doesn't give you real safety, since you can do: var c Color = Color("fnord") This comes up whenever you're handling foreign input. JSON unmarshaling, for example. You can override the unmarshalling to add some validation, but it won't solve your other cases; it will always be possible to accidentally accept something bad. Not to mention that the "zero value" of such a value is usually wrong: type Color string var c color // Empty string, not valid! A more safe way is to hide the value: type Color interface { isColor() } type colorValue string func (colorValue) isColor() {} var Red Color = colorValue("red") // etc. Now nobody can create an invalid value, and the zero value is nil, not an invalid enum value. But this is unnecessarily complicated and shouldn't be necessary in a modern language in 2019. The case of genuine sum types is worse. Your best bet is to use sealed interfaces: type Action interface { isAction() } type TurnLeft struct{} func (TurnLeft) isAction() {} type MoveForward struct{ Steps int } func (MoveForward) isAction() {} There are some downsides. You have to use static analysis tools (go-sumtype is good) to make sure your type switches are exhaustive. You get a performance penalty from having to wrap all values in an interface. And if you're going to serialize or deserialize this (e.g. JSON), you will be writing a whole bunch of logic to read and write such "polymorphic" values.
- gen220 7y agoI know it’s not a proper part of the language, so it might qualify a “hack” solution, but one can get around some of the issues you describe by defining your enums in protobufs, and using/trusting the generated code, no? It’s not a pretty solution from a language design point of view, but it’s been more than effective for us from an engineering point of view: since we’d need those proto definitions anyway, why bother writing our own?
- gfodor 7y agoWhen I used to work on PHP, we had a similar appreciation for this, and called it “the joyless programming language” (as a complement)
- BurningFrog 7y agoAs someone who thinks readability is the¹ most important quality of source code, that makes me less interested in learning Go. ¹ Yes, even over correctness.
- tfha 7y agoThat's really strange to me because I find go to be very readable. There are so few approaches to each of the basic programming building blocks in go that once you have read a moderate amount of idiomatic go, everything else just feels easy.
- dnautics 7y agoThere are far more readable languages in go. Go also encourages nested conditionals which can become a nightmare to trace when you're under duress.
- BurningFrog 7y agoI'm only responding to my parent post's arguments. I don't know much about Go myself.
- teunispeters 7y agoaesthetics are relative, not absolute.
- BurningFrog 7y ago
- Touche 7y agoStrong agree. I feel like the opposite is true of the Rust community. I follow a lot of prominent people in Rust and all they tweet about is intricacies of the language and new features/libraries. I'm not sure these people are even building anything, they seem to be "snacking" on the language only. You don't see people in Go doing this, and it gives the impression that the community is small, but I think they're just building stuff.
- pjmlp 7y agoBecause they are busy copying code around and following up on Java 1.3 footsteps.
- deleted 7y ago[deleted]
- steveklabnik 7y agoPeople build libraries to solve real problems. Language features need to have proper motivations in order to be accepted; we have rejected more academic features that don’t have direct uses. For example, async/await solved a real pain point for our largest production users, and that’s why everyone has been talking about it. Sometimes, these connections can be unclear from the outside. For example, there’s a lot of talk about “generic type constructors” and “generic associated types”, which sounds academic. However, it’s something the compiler needs to understand in order to implement a very simple user-facing feature: async functions in traits. From the outside it may look like “oh those folks are out of touch” but it’s directly connected to real user needs. (As a further aside, these two features are identical, but “generic type constructors” focuses on the academic, and “generic associated types” focuses on the end-user benefit; we changed out terminology here specifically to focus on what users get out of the feature rather than the type theory implications.) Furthermore, some people tweeting about things they’re excited about does also not preclude others who are heads down all the time. You wouldn’t see them for the same reasons, they’re not tweeting. These kinds of swipes against other languages lower the discourse and promote animosity when there really should be none. I’d encourage you to consider if these kinds of attitudes help bring about more people who are interested in building cool things, or fan flame wars that distract folks from doing exactly that. Every minute spent arguing over whose language is better is also a distraction from building cool things as well.
- cgag 7y agoI used to be a Haskell type and now enjoy Go greatly for this reason. There was a thread on the Rust reddit where someone was asking how to do something relatively simple using some elaborate combination of map/reduce/filter/continuations/who-knows-what, and someone said "just use a for loop", and the OP was enlightened. People don't know how great the burden of trying to model their problem to fit a fancy language is until it's gone. I didn't. I want generics and sum types, but I miss them less than I would have predicted.
- neandrake 7y agoI’ve seen a similar effect in myself and others at work but I don’t think it’s only a symptom of the language. After we switched from java 6 to 8 a handful of devs including myself went overboard modeling problems to be solved with streams API when it wasn’t necessary. These days it’s leveled out and use of the api is on a much more appropriate level. I think this is a process of learning. While learning a new tool you start to model problems so you can practice, even though not necessary. Once comfortable you realize when to use the tool and when not.
- steveklabnik 7y agoThis topic is more complicated than “for loops good, iterators bad.” I absolutely agree there’s a time and place for both; that’s why we included both in the language. But sometimes, iterators have less bounds checks than for loops do, so they can be more performant than a loop. Sometimes they’re the same. Depending on what you’re looking for, the details of what you’re doing, and your literacy with various combinations, different ways of expressing the same idea can be good. It all just dependents. (Also, rust’s for loops are implemented in terms of iterators; they’re actually the more primitive construction in a language sense; a while loop with the Iterator library API.)
- derefr 7y agoSure, and then you ask how to fold over a tree or an infinite stream, and the answer is to reimplement all the HOFs from the “fancy languages” in a type-specific way, because otherwise every user of your ADT is having to write not just a for-loop, but an entire push-down automata. I also write Go code without missing generics, but that’s because I’m also fluent in other languages, and tend to use those when I want something ill-suited to Go, rather than trying to force Go into that shape.
- matheusmoreira 7y ago> Most of it is wasted intellectual effort. It is highly stimulating intellectual effort though. Sometimes I sit down and spend hours just thinking about the best way to do something. It's some kind of philosophy: the abstractions we create reflect the way we understand things. To write good code, we must study the computer science and the problem domain itself. Without this, it's just boring mechanical work. Once the project has been figured out it ceases to be interesting. Some of my projects are unfinished because I can't justify spending more time on them even though I know exactly what must be done.
- stiglitz 7y agoYes, this! If you walk slow, then you walk in a straight line to the destination.
- SEJeff 7y agoI'm just going to leave a direct quote from Rob Pike: """ The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. """ I read that as Rob Pike saying he wrote go for idiots google hires to write. https://bravenewgeek.com/go-is-unapologetically-flawed-heres-why-we-use-it/ https://bravenewgeek.com/go-is-unapologetically-flawed-heres...
- _ph_ 7y agoNot idiots, but young coders who don't have 10-20 years of experience, who are required to write good code pretty quickly. So you want a language which is not only to quickly pick up but also quickly to learn to a point at which you are writing good programs.
- andrepd 7y agoMany programmers using a more "clever" language like haskell will spend a lot of time trying to make code more readable, searching for the right abstraction. Most of it is wasted intellectual effort. I rather disagree that making your code readable and maintainable is a waste of effort.
- Paddy3118 7y agoCode is helped by being readable. Saying your code is going to "look like sh£t anyway" seems rather defeatist, and an _excuse_ to write unreadable code.