Examples of beautiful Go?
programmers.stackexchange.com
programmers.stackexchange.com
I believe this to be a win because learning and writing the language are fast. There are some nice 'a ha' moments (such as when you understand interfaces), but mostly the code is (in a good way) boring.
It's also a small language. I think it was designed for people who want to get things done, not for people who want to find the cleverest (i.e. least-number-of-characters) way of doing it.
The way concurrency is built into the language (based on sequential programming with channel based communication) further reduces the potential complexity. You just write sequential code and let the Go runtime handle the rest.
If you look at typesetting, a lot of care is taken to tweak letter spacings and getting the font just right. This makes the work easier to read than a giant wall of text, and visual beauty comes as a natural side effect of that optimization.
Code really isn't any different. While there is certainly some variance by the beholder, code that is generally easy to read is going to naturally look beautiful. Additionally, and perhaps more importantly, if the code is beautiful, it will attract you to maintain it.
I am still a Go newbie, but my initial impression is that certain constructs make it more difficult to write beautifully than it should, which only serves to detract from the readability (not talking clever one-liners here – which shouldn't even come up in beauty discussions). However, given that I am new to the language, I may just not be yet able to express things as well as I could.
Beauty's more subjective than readability, but most of the time a beautiful, elegant, recursive structure takes more time to figure out than your ho-hum for loop, in my experience at least.
It depends very much on what you want to write. If you want to write something related with File I/O or Network I/O in a reliable fashion, chances are that you cannot see it more beautifully implemented than in Go. No big surprise, the language designers are known for being involved in a lot of Unix stuff.
On the other hand, if you want to implement a linear algebra package that has support for a lot of structures, you will see less beautiful code. The same is somehow true for a number of algorithms that act on collections. There are no templates, so you either Copy&Paste or you work with interface{} worst case, meaning such code is cluttered with type assertions.
But besides that, I think most Go code can be made beautiful. It definitely has its sweet spot in Server Programming.
Coming from a background in Lisp, I don't find much in Go that seems very 'elegant' or 'beautiful'. What I find is that everything - including the Go source code itself - is perfectly standardized (and therefore immediately readable).
I don't have to worry about different coding styles, about semicolons vs. no semicolons[0], about having more than one way to express the same code. I can skim the code easily because the structure is so uniform across all standard and community libraries.
That's a different kind of beauty from chaining macros and cons cells, but it's beautiful nonetheless.
Which, btw, gofmt does does pattern replacement.
Combined with structural regular expressions you can answer questions like "what are the types that contain both a mutex and a map, and what are the types that contain the types that use a map without a mutex": http://pastebin.ca/2202690
http://talks.godoc.org/github.com/sunfmin/talks/2013/gofmt.s...
Slide 5 is the proof this is no better than search and replace.
This is a much better example: http://research.swtch.com/gofmt
gofmt -w -r 'x[i:len(x)] -> x[i:]' *.go
Note that this is not a regular expression... it's looking for go expressions that match that pattern.
So, for example, this will catch this:
a := charts[start:len(charts)]
and convert it to this:
a := charts[start:]
You can design your regex around this, but at some point, you'll end up just reinventing a Go parser inside a hideous, unmaintainable regex. So you might as well just use gofmt.
It's also not very spectacular, since it's what Java IDEs have done for ages, plus incremental compiling :). (Or code analysis tools such as Sonatype.) Go is just playing catch-up here.
The one thing they did very well is dictating the code style with gofmt. You may not like it, but at least there is once standard. Though, one wonders how many developers and businesses use whatever layout Eclipse or Netbeans use out of the box ;).
1. runs fast
2. uses little memory
3. is easily made (or is) concurrent / parallel
4. is transparent (the intuitive run time complexity of operations is apparent, no hidden O(n..) operations, etc)
5. is easy to maintain
If this code happens to be more verbose than say Java, so be it, it is still beautiful. On an anecdotal note, most large C++, Java and node.js projects I have ported to Go have ended up being fewer LOC in Go.You can cross compile as well. IE compile for Linux from OS X.
http://dave.cheney.net/2012/09/08/an-introduction-to-cross-c...
That said, it's certainly much cleaner to just build static binary executables like Go does.
Also, since there is no version management in Go packaging, you better hope that every program uses the API of the latest or fixed version.
Imagine that a shared library were upgraded, and it contained a new vulnerability. This vulnerability now automatically exposes any program that uses said said library.
Trade offs :-)
(Compiling Go is fast. Re-rolling a binary isn't a Big Deal.)
> Also, since there is no version management in Go packaging, you better hope that every program uses the API of the latest or fixed version.
Simplicity has a price. But there's no reason why an external tool cannot be made to handle versioning.
Production systems normally only replace shared libraries to fix vulnerabilities. So, that's a non-argument.
On non-production systems, it's far easier to replace one vulnerable library, than tens of vulnerable Go programs. That is, if you know which Go program was using what version of that package again.
Vulnerabilities are usually unforeseen. Which means that upgrading a shared library---for any reason whatsoever---could introduce a new vulnerability to all programs using that library.
> On non-production systems, it's far easier to replace one vulnerable library, than tens of vulnerable Go programs. That is, if you know which Go program was using what version of that package again.
It's easier, but it comes with aforementioned cost. Also, relative difficulty matters here. Maybe you haven't used Go before, but that all seems pretty simple. Particularly given the tools to analyze Go programs built right into the standard library.
Also, FYI, shared Go libraries are on the roadmap. That doesn't really help, but it's certainly better than "we refuse to add it."
I would rather replace all the binaries separately. If one of the new binaries didn't work, I could go back to the old version.
I do agree that the shared library approach uses less network bandwidth, but is that really a big problem these days? Unless you're sysadminning over dialup, chances are you don't really care. The size of binaries has plateaued at a meg or two even as networks have continued to improve.
For general purpose use, this is a huge deficiency in Go.
I also don't really agree with the "fire-fighting" approach to security. Developers are constantly removing security bugs, but they're also constantly adding them. Result: the system is always vulnerable. Why should we copy a failed approach to security, when there are other approaches like sandboxing and process isolation? (By the way, these other approaches are a good idea in C/C++ as well.)
Shared libraries are also on the roadmap for Go so at some point that will be a possibility.
What is the basis for your claim? That it's impossible to build large projects without static typing, or what?
I work in enterprise projects done in multi-site context context, usually with teams that encompass more than 30 developers with multiple levels of experience.
Depending on the customer, sometimes it is very hard to force developers to write unit tests. If the customer does not care about them, the product manager will not enforce them.
Additionally given the lack of experiences of the junior developers, always chosen because they are cheaper to the customer, unit tests tend to be badly written.
So with static typing one has at least a guarantee that the product compiles and might eventually even run.
With dynamic typing and lack of tests, you can spend days getting the product back into a runnable state.
I do believe the Go code is more maintainable and I like the fact it is statically typed.
Looking at the Benchmark Game Go vs Java7[0], why is Java 2x to 8x faster than Go for many benchmarks? Is the jvm that optimized these days or are those Java7 programs super tweaked for performance?
[0] http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?t...
Go is a very young language so while very fast compared to an interpreted language like Ruby, Python, etc by virtue of being compiled, it going to take some time be be on par (in terms of execution speed) with a Java, a JIT language that has had 18 years to mature.
It says a lot for Go that its execution time is on par with C#: http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?t...
My impression is that Go and Lisp are similar here. Neither is particularly pleasant to read in the small. Lisp because of all of the parens and the sometimes "backwards" dataflow of nested functions. Go because of the "imperativeness", the frequent manual returns for errors, the mandatory {} for blocks.
In both cases, that was a deliberate choice in order to get simple syntax. The beauty they are focused on is larger-scale beautiful semantics. Lisp with macros, first-class functions, etc. Go with interfaces, goroutines, and channels.
Where some languages treat the syntax as a first-class feature, I think Go and Lisp treat it more as a means to an end. That being said, in both cases, practitioners of those languages do specifically like their syntaxes. It's just not a major focus of the language designers' time.
I've come to believe that the second part of this statement really is the primary reason why Lisp drives some people absolutely nuts. The parens are really just a distraction and "easy" justification for not liking the language.
It makes sense, though. When you have to read both "outside in" and "bottom up", a lot of your acquired reading abilities (left to right, top to bottom) are in tension with the code you're trying to understand.
At least in Clojure, the ->> family of operators ("threading" macros) are doing a good job helping to alleviate this. Maybe they're available in other Lisps, I don't know.
EDIT: found a reference to these macros in Emacs Lisp: http://emacswiki.org/emacs/ThreadMacroFromClojure
Agreed. This is a major reason why Lisps read wrong to me and is one of my favorite things about conventional subject.verb(object) syntax in OOP languages.
My own language[1] has Lisp-like semantics where methods are lexically-scoped and not attached to classes but retains something close to OOP syntax because I think that's such a readability win. For me, this is the best of both worlds.