Though I find some of the design choices a bit frustrating, e.g., generics, lack of min/max; for each design choice I disagree with there are dozens of other choices I do agree with. Structural typing, built-in concurrency, a rich standard library, the syntax, the list goes on.
There is a certain brutal elegance to the way the Go standard library is designed, it is really easy to read and comprehend. It took me less than half an hour to make sense how the net/http server was implemented.
This doesn't only apply to the standard library: because the language style is standardized--there are no coding styles, there is just a coding style--other libraries or programs are very easy to understand. If the program architecture is easy to understand, it doesn't require any significant domain expertise to understand.
Languages aren't supposed to be cargo cults, though some culture can be nice, let's not kid ourselves: programming languages are means to to an end. Go is pragmatic, it gets things done, it is a tool, and most importantly, it doesn't get in the way of design or architecture. You are free to build programs in any style or way you want.
Ultimately, languages are just expressions of grander designs that are far more important than arguments for and against a particular syntax or language feature.
---
> Go programs are built from just their source, which includes all the information needed to fully build the program.
Still have to deal with GOPATH, vendor your dependencies, and have everything a `go generate` comment wants to invoke. It's certainly better than makefiles, but it's hardly just the source.
> C# is joined at the hip with Windows. Objective-C and Swift are for Apple. Java and Scala and Groovy might benefit from JVM bytecode and its independence… until you realize that Oracle isn’t interested in supporting Java on anything other than Intel hardware.
C# has Mono, you can use Objective-C with gcc, the JVM has a bajillion implementations. I suppose Swift is more or less unportable at the moment. 1 out of 4 ain't great.
> Go is helping pioneer a command-line renaissance that reintroduces a generation of programmers to the idea of writing tools that fit together like segments in a pipe (the original Unix philosophy).
This never went away. Heck we were going over this in college, which for me was in Scheme, Java, C, and C#.
True. Go is a good language for server-side web stuff. That alone is enough to make it very useful. It is a good language for when you want to get server stuff done and it has to go fast. That's enough. It doesn't need to be overhyped.
Go is helping pioneer a command-line renaissance that reintroduces a generation of programmers to the idea of writing tools that fit together like segments in a pipe (the original Unix philosophy).
That's inherently a batch processing approach. The "strings through a one-way pipe" approach was never that great. It's brittle. If anything goes wrong, the options are to abort the whole thing, or print a message to stderr which will probably be ignored.
Better ways to plug programs together are needed, but Go doesn't do much new in that direction. Today, plugging programs together probably involves a number of programs which use protocol buffers to communicate, running on, say, Amazon AWS. There's much work going on in tools for doing that. They aren't typically pipe-oriented.
Yes! Maybe a language with direct support for software architecture, such as defining + (re-)using your own connectors.
That tells me that the don't think they can win the comparison.
It's like saying a jeep can't compare to a ferrari or a minivan - other than having 4 wheels, they're really not at all designed to do the same kinds of things... Sure, maybe driving to the corner store they're all pretty much the same, but which do you want in 8" of mud? Which do you want in a car chase on the highway? Which do you want to bring your 4 kids to soccer practice?
The reasons we aren't seeing more lisp/clojure are not technical/objective ones but attitude, social and nework factors - which are just as relevant though.
Once, when you install Go.
Then for each project, put your stuff where you want and symlink from $GOPATH to where it is. Once per project.
There really is minimum hassle to integrate the recommended flow.
GOPATH isn't awful, but it's something more than "just source" as claimed in the article; which is my point. Go is simple, but not as simple as claimed.
it is actually more difficult to code in a language that's simple
Why? Because a more feature-complete language allows you to ELIMINATE the concept of `nil` through an `Option` type.
An `Option` type is an `enum` that consists of either `Some(x)` or `None`. That means it is always checked. You can never accidentally use a value that is `None` because the type checker would not let you use `Option<T>` instead of `T` itself.
The code snippets on the websites are far from simple. You HAVE TO remember to do `if err != nil` in every single function. A more advanced type system would actually make this a requirement.
So what is more important, the simplicity of the language or lack of bugs?
I don't think this is a bad thing in and of itself - I like how languages like Python, Lisp, Javascript, and Erlang have been able to layer typesystems on top without building them into the language itself. But I wouldn't exactly hold it up as an example of simplicity, particularly since in those languages the community hasn't agreed on any one type system.
How is approaching every single problem with a different tool more simple than using the type system as the one tool for static checking? The Go ecosystem is creating a ad-hoc, informally-specified, bug-ridden, slow implementation of half of the type checker of Haskell.
It's a tradeoff between designing your own complex building blocks or having very complex tools with thousands of them ready-made.
I won't pretend to have an easy answer, but I do know I personally prefer when people err on the side of simpler tools.
> Q: Lack of generic collection classes like in Java’s Guava library?A: For 90% of cases, slices and maps do what you need. For the other 10%, you might consider whether your package should own the logic of those special containers, instead of using an external package.
I read this as:
"We're not willing to put in the hard work on thinking of a decent generics implementation, despite decades of working solutions with a myriad of choices in tradeoffs, so you'll have to do the hard work of integrating a dozen different collections libraries and who knows how many different, after-the-fact, mediocre strategies to the generics issue, each slighly off, all of them conceptually incomplete somehow, and with the slight bugs that come from not having a well-trodden path for an essential component of most programming languages."
I can wait for features; I'm patient. But the downright refusal of Go's core team to even begin to address this issue is not just a technical problem, but a communications one. This is clearly big for a lot of people, and I have yet to see any serious responses aside from either "you domain model's wrong if you need generics" or "deal with it".
In contrast (just picking this language for its community outreach, not because of any perceived technical competition), Rust's core devs have been forthcoming about practically every objection they've gotten. Their answers are clear, detailed, not demeaning, and constructive. When they don't know, they're honest about it, and when answers are hard, they take the time to explain. But I have never heard of anyone being belittled for not understanding lifetimes or the borrow checker.
Yet it seems that somehow if I have a bone to pick with Go not being able to dispatch functions by argument or arity, or with how its simplicity ends up with codebases a lot of people would consider much more verbose than necessary, it's just that I don't "get it". The problem isn't even in the accusation; I just don't even receive examples or reasonable explanations, something I'm used to in related language discussions.
I was willing to give the language a pass on these things when it was just getting started and a lot of the classical, early product criticisms were abound. But it's gotten tiring; the number of unanswered questions is remarkable by now.
No sane person would read "I read this as" and assume that this is in fact exactly what the speaker said, nor should any speaker assume "I read this as" is meant as a defamation or misquote.
In addition, you being a contributor not a core team member does not change what you said in the least. A language's design, even if it seems like there's a tight core, is ultimately decided in part by all contributors and the community around it as well.
Also, "Sans runtime"? When did that happen? Last I heard, Go had a fairly substantial runtime to it, making it unsuitable for many places, and not trivially possible to just link right into any old program. And without a runtime, it'd be hard to have a GC, eh?
I think you meant "Statically linked", and nothing at all about runtimes. Rather large difference. FWIW, you can statically link many things, including C#. Which is how C# is deployed e.g. in bestselling iOS apps (and running on iOS has gotta be far from being "tied to the hip" of Windows).
(As a comparison, Rust is actually what is generally meant by "sans runtime", as in you can just call right into a Rust function without setting up anything else (just don't like, call panic or something).)
Which "working" solutions? Has anyone solved creating a type system that offers OO/inheritance, generics, mutability and isn't mind-bogglingly complex?
What is your definition for a "working" solution? I don't think any of the current languages we have fit this description, since all of them are capable of producing type errors that are way too complex to comprehend.
> with a myriad of choices in tradeoffs
Yeah, but how do you decide on which tradeoff to settle for? And why is the tradeoff to not engage in this mess not a just as valid one?
> so you'll have to do the hard work of integrating a dozen different collections libraries and who knows how many different, after-the-fact, mediocre strategies to the generics issue, each slighly off, all of them conceptually incomplete somehow
You're missing the point. Solving the generics issue for a specialized case, even if just on a library-level, is much easier and has much less impact on applications than solving the general case and forcing that solution onto every bit of code in that language.
I don't think the author is advocating using a general purpose 3rd-party library for containers, but rather writing your own special-purpose ones for the few cases where the on-board tools aren't enough.
> with the slight bugs that come from not having a well-trodden path
Localized bugs are easier to solve than the type errors common to that "well-trodden path" you talk about, which may span an entire application.
..not that there is any consesus on what exactly that "well-trodden path" is, since as you mentioned there's a myriad of tradeoffs.
Pretty sure OCaml would match your definition of "not mind-bogglingly complex" as well as "type system that offers OO/inheritance, generics, mutability".
Run trace on a Linux binary and see the slew of "file not found" errors from syscalls looking for shared libs at startup and then imagine each one of these is taking place over a 9600 baud connection.
Good design realises benefits that authors never needed to consider.
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=54185, ...}) = 0
close(3) = 0
open("/lib64/librt.so.1", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@!\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=43880, ...}) = 0
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\356\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1921176, ...}) = 0
close(3) = 0
open("/lib64/libpthread.so.0", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340]\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=142640, ...}) = 0
close(3) = 0
open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=99158576, ...}) = 0
close(3) = 0
open("/etc/localtime", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=3661, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=3661, ...}) = 0
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\7\0\0\0\7\0\0\0\0"..., 4096) = 3661
lseek(3, -2338, SEEK_CUR) = 1323
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\10\0\0\0\10\0\0\0\0"..., 4096) = 2338
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
write(1, "Tue Feb 24 08:57:58 GMT 2015\n", 29) = 29
close(1) = 0
Oracle does support the JVM on embedded hardware. It's right here: http://www.oracle.com/technetwork/java/embedded/embedded-se/...
Also since when was Oracle the only JVM vendor. There are plenty of others that support different hardware: http://en.wikipedia.org/wiki/List_of_Java_virtual_machines
And finally since when has the choice been between slow, interpreted languages and fast, compiled ones ? Plenty of options exist in the space between.
Go doesn't want to use the concept of generics. However, if your code uses "List<IP>" instead of "List", it is easier to understand, because it additionally tells you it is about IPs. Python is a language which tries to be easy by resembling pseudo code.
If you really want simple, you could as well use SML, TCL, or Lua.
And if you truly want simple, you use Smalltalk, Scheme or Forth.
How many times do we have to read this... It's not that Go doesn't want to use generics, it's that the use of generics doesn't come for free.
For generics to be introduced, the gain have to overcome the costs. It seems we're not there yet (I'll be honest, I didn't follow all the arguments closely)
That's interesting. Has Canonical stated what their interest in Go is?
"Something that is simple may take longer to write and might be more verbose" -- from the article.
In a simple language, there should not be functions that do exactly what you want to do; that would be a sign of the language being complex and featureful, which are enemies of simple.
(and yes, this is a bit tongue-in-cheek)
An alternative would be to use [16]byte as your map keys and then subslice the array for net.IP.
Lack of generic access to data structures is one of their bigger fails.
However, they don't see it that way. One point of Go was to prevent needing to describe things before being able to compile it. Most things that people regard as "failures" in Go were deliberate choices to enable large codebases.