I think I understand where the Go team is coming from, but there is so much hype from other quarters that I think is at best premature.
I think Go will be a fantastic alternative to C for "systems" programming, once it matures. I think this was one of its most immediate original goals, but really it is not yet ready to do even that. Two main reasons it is not ready for that is that performance is not yet there though I think it will get there; second reason is that it has no support for dynamic linking or code loading. You can't write something like Apache, or even Nagios in a platform that has no support for loadable modules. Still I think there are LOTS of applications where these constraints would not be an issue.
So then we have all this hype and people doing things like building web applications in Go and calling it the next Node.js or Ruby or Python and frankly this is just stupid. Despite some claims it has nowhere near the expressive power of those languages. It is statically typed but has no Generics - and there are quite a few use cases where there is no answer(https://groups.google.com/forum/#!topic/golang-nuts/PYJayE50...) and you will just have duplicate code.
It polymorphism but no inheritance - and the way interfaces and composition works while still fairly powerful is not at all what an OO programmer would expect(http://xampl.com/so/category/software/golang/) just naively reading about this feature. Whatever you want to say about the benefits of simplicity, this is LESS powerful than any OO dynamic language. Go's closures are nice, but aside from that on what basis can any other claims be made about its expressive power?
In fact, the language is intentionally very, very limited. They want large teams of mediocre programmers [1] to produce code that is very supportable. Everytime someone in the lists brings up a "why don't we have" question all the answers revolve around this premise, that the language becomes "too complicated" if you have crazy features like generics. So it seems Go is optimized to satisfy the angst of systems programmers who have to work on C code that is thirty years old and been maintained by dozens of people. I get it, but that doesn't mean its really a good language for hackers to use to build the latest social network for people who like cats.
Ok so concurrency. Yes Goroutines are smart. Channels are smart. These are great features. Scala can do the same thing (and more elegantly) with it's Actor constructs. The same thing can probably be done in other languages such as Ruby (https://github.com/igrigorik/agent). Its nice that this feature is in Go from the beginning, but its going to make its way into other platforms as well.
Performance. Well...lots of blog articles just keep repeating the mantra of "native code" as though it automatically means something. It doesn't. C isn't just fast because it generates native code, it is fast because it's compiler has been optimized for decades. Go doesn't have that today. In fact today, Go is slower than Java and Scala based on both Google's benchmarks (http://www.readwriteweb.com/hack/2011/06/cpp-go-java-scala-p...) and the language shootout (http://shootout.alioth.debian.org/u32/which-programming-lang...). Go will get there almost certainly, or at least very close, but it isn't there today.
Today if you want a static, fast language with good concurrency and an incredible standard library and extended library ecosystem then you want Scala I think. If you want a powerful, expressive language for quickly building web applications then you want Python, Ruby[2] or Node.js or similar. If you want a language is actually very fast and with incredible libraries you want C/C++.
I really do not hate Go. But it is not yet ready to replace hardly anything in my opinion.
edit:
[1]You are right. The "mediocre programmer" comment is really out of line and a major distraction. I guess I actually think this IS a feature of Go: more maintainable code than C/C++ from mediocre developers. I'm bitter and tired ofdebugging twenty-year old C programs and I wish they were all written in Go. I shouldn't read that into Thompson's or Pike's intentions though.
[2]May brain stopped working here - I meant Ruby not Java.
"They want large teams of mediocre programmers to produce code that is very supportable."
I think any argument that uses the phrase 'mediocre programmers' should set off alarm bells by now. Its critique by slander. The creators of the language (who are not mediocre) like simple languages. The desire to make things more transparent and simple is does not reflect on a person's capacity it reflects on their taste (insert Einstein quote, St Exupery etc). Ken Thomson is not an idiot for example but:
"Computer: Your nominators and endorsers for the Kanai Award consistently characterized your work as simple yet powerful. How do you discover such powerful abstractions?
Ken Thompson: It is the way I think. I am a very bottom-up thinker. If you give me the right kind of Tinker Toys, I can imagine the building. I can sit there and see primitives and recognize their power to build structures a half mile high, if only I had just one more to make it functionally complete. I can see those kinds of things.
The converse is true, too, I think. I can't from the building imagine the Tinker Toys. When I see a top-down description of a system or language that has infinite libraries described by layers and layers, all I just see is a morass. I can't get a feel for it. I can't understand how the pieces fit; I can't understand something presented to me that's very complex. Maybe I do what I do because if I built anything more complicated, I couldn't understand it. I really must break it down into little pieces."
Is he an idiot because he couldn't understand it? Or they giving awards to the wrong people? Could it be that to recognise everybodies limitations when working with code day in day out (not just the mediocre people) is actually a valuable insight?
Also this quote explains the design of Go (even thou he said it in the context of Plan9/Inferno)[1]
"Thompson. The aggressive use of a small number of abstractions is, I think, the direct result of a very small number of people who interact closely during the implementation. It's not a committee where everyone is trying to introduce their favorite thing. Essentially, if you have a technical argument or question, you have to sway two or three other people who are very savvy. They know what is going on, and you can't put anything over on them."
Rob Pike says the same thing. "All three of us need to be convinced for a feature to make it into the language"
You are right, Rob Pike said it was for "large teams". In most places (maybe not Google) "large team" will often mean an average skill level that is mediocre. I do not understand why a large team of very skilled programmers can't build large systems that are very maintainable in more powerful and expressive languages. I do not understand the argument that a language is "too complex" in this context.
edit: You are right and I added this to my top-level comment. The "mediocre programmer" comment is really out of line and a major distraction. I guess I actually think this IS a feature of Go: more maintainable code than C/C++ from mediocre developers. I'm bitter and tired ofdebugging twenty-year old C programs and I wish they were all written in Go. I shouldn't read that into Thompson's or Pike's intentions though.
Whenever I hear someone denigrate simplicity in a language as something that caters to 'mediocre' or 'average' programmers, I'm reminded of the surveys where 80% of people think they're an above average driver.
>The creators of the language (who are not mediocre) like simple languages.
They are surely not mediocre programmers (or mediocre contributors to computing).
They are however mediocre in their modern language design skills. What are their language credentials?
Lars Bak is a compiler/language guy. Wirth was a compiler/language guy.
Hejlsberg is a compiler/language guy.
The Go guys? Not so much.
Having designed the best systems language 40 years ago is not a sure sign that you can also design a good systems (or not) language 40 years later. Compiler design has changed a lot since then, but it's not like Go has a lot to show for it (ok, they also wrote 1-2 other ho-hum languages, like the Plan9 one).
Plus, beside the language specs, there is also the compiler. Did those guys ever wrote a top-notch compiler? The original C compiler wasn't the state of the art in the language. It took lots of efforts and different companies to get to the advanced optimized C compilers we have today.
Plan 9 was far more impressive a feat than Go was, and much closer to where the team's skills are.
Fair points but I wrote the response above saying that its not really about ability, its about taste. Neverthelesss I had to assert something about the Go language designer's ability in order to refute the top post about mediocrity. Now you are back to talking about ability and credentials. We could go back and forth all day. The point is that Go is the first mainstream systems language to focus on leaving stuff out. Think about lisp, its beautiful because its really just one thing but you can do everything with that one thing. Go is not the same but the aesthetic is similar. It's a question of taste. Do you like a powerful tool with loads of abstractions? Or do you like to strip everything down to the metal and see what is the minimum you can run with and still retain most of the fire? Some of those things that you leave out may turn out to be those things that have come out of "modern language design".
Scala has many modern language features yet Martin Odersky said, of Go "I like a lot of the design decisions they made in the language. Basically, I like all of them." He can appreciate it because there is also an aesthetic in Scala that focuses on a minimal, simple, consistent core. But Scala has a shell with a bigger surface area. Depending on the context and the kind of person you are you might want one or the other but there is no point in criticising Go by saying "Its missing feature X". That's like criticising a race car because it doesn't have a sat nav. It still does 80% of what a normal car does but it does the core things that a car does really well and its not just that you have less weight its also that you can focus on driving it because there is less to distract you. (Where driving == building cool stuff faster because I'm not constantly getting distracted by thinking about how to make the code more clever ('Hmm maybe I could metaprogram that and save another three lines')). In general the simpler car is also easier to fix and easier to understand (these attributes are more important in programming languages than in cars, no matter what the ability of the programmers (cognitive load)). Of course there is an argument for having a sat nav in other contexts.
There are some rather clever solutions to this problem that don't require the complications inherent in dynamically linked binaries.
It's easy to forget that, even on commodity hardware, Go (`gc`) compiles "Fast (tm)." -- Even for large projects.
Take a look at the Revel framework[1], it's a young project but it aimed to port the Play framework to Go.
Once you dig into the code, you can see the framework hot-loads your web-app by altering the source code of the framework (to add imports for your app) and then recompiling on the fly.
The framework can then do all sorts of things; one that I like in particular is that it seems to be able to catch panics at the top-level, and relay a nice stack-dump to the browser (in debug mode).
All the while you still have a statically linked binary running.
---
tl;dr: There are other solutions to the problems dynamic linking solves, without actually using dynamic linking [2].
The lack of generics makes Go unsuitable to write typesafe, reusable data structures for use in many popular algorithms. That's a fact.
Yes you can hamfist your way around interfaces and empty interfaces, but it's a complete and utter chore and requires a lot of code duplication for each individual type you want.
I like Go in the sense that you can write things quickly and cleanly, but for the use case above, it's not appropriate.
Yes well, I was writing a comment that was already too long. Lots of comments could be better supported. I suppose I could develop this into an original piece with lots of code examples. I guess if I don't do that, I can't write any comments?
You should have picked one point you wished to make and argued it. For instance "Scala can do the same thing (and more elegantly)" could have been demonstrated with a couple of examples. I have not used Scala, but I would be interested to see examples of language-level concurrency other than Go.
First, you're saying Go will be an excellent systems programming language, which is its main stated purpose. To me, it already means that it's something that's worth looking at.
Then, you claim it doesn't work like established alternatives, so it must be over hyped. Like anything new, it's normal to see hype, misinformed opinions, advertising and enthusiasm about it.
I haven't written a line of Go, but it's going to be the next programming language that I learn because I find its concurrency model very compelling.
>you're saying Go will be an excellent systems programming language, which is its main stated purpose
It will be, when it is finished. Without dynamic linking it cannot replace C/C++ for lots of the intended use cases.
>Then, you claim it doesn't work like established alternatives, so it must be over hyped.
No, I claim that it lacks most useful features of the powerful languages it is constantly being compared with. Rob Pike says you don't give up much expressiveness moving from Python/Ruby to Go. This is absurd. Go has a different way of doing things yes, but for many things, that different way is to write more code.
I think in real usage (in my experience), this is very rarely the case. The only thing Generics are really extremely useful for is writing data structures that you can re-use with multiple data types.
Also, I tend to appreciate Go's approach of writing more code in these situations, because in general the code is still far more readable than a C++ Template (for example).
The Go creators have not ruled out adding Generics, they are simply being very careful about how they implement it. I would rather have this situation than something like a generic Java/C#/C++ style generics implementation rammed in because it is demanded by people who have hardly touched the language (or refuse to touch it until said feature is added).
"I think in real usage (in my experience), this is very rarely the case. The only thing Generics are really extremely useful for is writing data structures that you can re-use with multiple data types"
When working in .NET on framework-y code I use generics with reflection a lot because it makes it easy to remove a whole swathe of repetitive code. Mapping from one object to another using a Mapper<TFrom, TTo> with generic constraints, reflection and some over-ridable conventions springs to mind.
Not an argument for adding generics in Go, or against Go itself as I haven't yet found the time to play with it. However I do think generics can be more useful than people give them credit for.
The thing is that Go has generics already for the most common data types: slices (vectors, what Python calls 'lists', others call them 'arrays'), channels (iterators, generators), and maps (dictionaries, hashes). Since there's already a built-in map[T1] T2, so you wouldn't need to build it yourself, which saves even more work.
If what it has are a handful of special cases baked into the language, saying the language supports it is probably overselling (similarly, having types like `float[3]' doesn't mean we consider C as supporting dependent types).
"The only thing Generics are really extremely useful for is writing data structures that you can re-use with multiple data types." Take a look at C++'s STL: a large set of generic algorithms that work with any data structure (built-in or user-written) supporting the standard iterator protocols. Many people think of STL as "a bunch of container classes", and ignore the algorithms.
... it cannot replace C/C++ for lots of the intended use cases
Does Go need to replace C/C++ in all use cases to be a "worthwhile" language? If not, then is there some required percentage of use cases that Go must be better at than C/C++? If not, then what is the point in arguing that there are some things Go is not as good for? Of course, knowing what those deficiencies are is important but it sounds like you are trying to convince people to not use Go. Why?
Yes there is a lot of Go hype around here and I understand your desire to counter that hype.
Choosing programming languages and other technologies and skills is an unfortunately subjective and messy process.
Loadable modules like the way Apache/nginx do, or Nagios or quite a few other basic infrastructure applications.
Another related issue (though not specifically dynamic linking) is with client libraries for server software. For example say you wrote something like Redis in Go. You write a Go client, then you want to start porting client libraries. You will be starting over from scratch because you can't expose your Go code through any kind of native interface. If you write client libraries in C it is pretty easy to create language bindings in all other popular languages. So if I wrote a server in Go that I wanted to distribute widely, I think I'd have to write the reference client in C.
> Loadable modules like the way Apache/nginx do, or
> Nagios or quite a few other basic infrastructure
> applications.
I agree this style of composition is not currently possible in Go, but I do not agree that it is necessarily a good (in the engineering sense) solution to the requirement(s) it solves. There are lots of other ways to communicate between dynamically dispatched blocks of code, and dynamic code loading is by any account fragile.
Or, to put it another way,
> you can't expose your Go code through any kind of
> native interface
I'm not convinced this restriction is anything but good.
But, as Go advocates will tell you any Generics related discussion in the golang list: "just try programming in Go for a while and you'll find you don't need Generics".
Why do they assume that people complaining haven't used the language "enough", is beyond me.
Plus it seems they believe that there is some amount of time of using a language, after which fundamental issues and well understood design issues disappear...
But, by that logic, why abandon C in the first place?
"Just use C long enough, and you'll find out you don't need garbage collection, built-in collections, closures and memory safety".
Or:
"Just use assembly long enough, and you'll find out you don't need all those fancy functions, and variables, and looping constructs, and stuff".
Yes exactly. Although I think with Generics they will probably come around at some point. If you read the link I posted they do recognized there are unsolved cases without Generics and while some people DO say "just rewrite your collections over and over when you can't use maps/slices", others recognize that this will lead to redundant code which is not something you want in a language touted for its maintainability.
Other problems though such as default function parameters are less likely to be addressed (it seems to me). This is a problem because it means lots of code will be written in which there are implicit defaults (just pass 0!) that are not well-understood or guaranteed by the interface. The answer there is "just write another method with a different name and parameters". Ok so now I have to think of sensible names for the methods that are exactly the same as another method except they apply defaults...and write and maintain more code of course - especially if its in an interface you re-use.
Today I was writing a Skein hash function implementation in Go. Skein can be configured for different purposes (e.g. MAC, KDF, stream cipher, etc.), and it can accept >5 optional arguments. I wanted to expose all these configuration option to users, in addition to providing a simple version that will create hash without configuration. Here's how I solved it (there's prior art in some standard libraries).
I exported this struct:
type Args struct {
Key []byte // secret key for MAC, KDF, or stream cipher
Person []byte // personalization string
PublicKey []byte // public key for signature hashing
KeyId []byte // key identifier for KDF
Nonce []byte // nonce for stream cipher or randomized hashing
}
and then provided a function:
func New(outLen uint64, args *Args) *Hash
Users can easily call it like this:
h := skein.New(64, &skein.Args{ Key: someKey, Nonce : someNonce })
I like this design a lot.
Plus, with structs as arguments you can easily provide more than one default configurations. For example, if you want to personalize hash for different usages:
var FileHashConfig = &skein.Args{ Person: []byte("file hash for MyApp") }
var MessageHashConfig = &skein.Args{ Person: []byte("message hash for MyApp sync") }
and use them:
h := skein.New(64, FileHashConfig)
(Note: Args is a bad name for general usage -- I called it like this only because Skein authors call these "arguments", and "configuration" is reserved for something else.)
There are a few specified usages for Skein with arguments, so to avoid making users of my library read the Skein paper, I wrote "constructors" for them:
// NewMAC returns hash.Hash calculating Skein Message Authentication Code of the
// given length in bytes. A MAC is a cryptographic hash that uses a key to
// authenticate a message. The receiver verifies the hash by recomputing it
// using the same key.
func NewMAC(outLen uint64, key []byte) hash.Hash {
return hash.Hash(New(outLen, &Args{Key: key}))
}
// NewStream returns a cipher.Stream for encrypting a message with the given key
// and nonce. The same key-nonce combination must not be used to encrypt more
// than one message. There are no limits on the length of key or nonce.
func NewStream(key []byte, nonce []byte) cipher.Stream {
const streamOutLen = (1<<64 - 1) / 8 // 2^64 - 1 bits
h := New(streamOutLen, &Args{Key: key, Nonce: nonce})
return newOutputReader(h)
}
So depending on the algorithm you are using, you are going to leave some of these fields in the struct as null/empty?
To me this is the whole issue. Of course if we have knowledge outside the method declaration about what values can be left 0, then we can pass the correct ones as needed. But the interface is ambiguous about when that is acceptable, and the implementation can change without changing the interface.
According to the mail lists I've read on the subject, the correct way to implement your algorithms in Go would be for each to be a separate method. And indeed that is the only way in Go to support a fully specified interface.
In most cases (but now all), there are better ways to design API than stuff them with default arguments, which make functions behave unexpectedly if you forget something.
But maybe we should talk about specific cases? What are the examples of functions which need to accept default arguments? Maybe I can try to convert them into something reasonable in Go?
That is awful for anyone attempting to re-implement your API. It is totally unclear what field combinations are valid, and in time the situation is only going to get worse.
There are no invalid combinations of fields in this case, any combination is valid.
If there were invalid combinations, of course, this wouldn't be a nice API.
This post by Russ Cox (a co-inventor of Go) describes leaving parametric polymorphism out as "slowing down programmers".
http://research.swtch.com/generic
I think it's safe to say they are aware of the issue and consider lack of generics a negative (because it slows you down, duplicates code, adds type unsafety, etc.). They don't have a solution they like yet.
I am not cargo-culting. I am saying that it is not invalid to say to someone 'you're doing it wrong, if you just use a different style that does not become a problem.'
I think I understand where the Go team is coming from, but there is so much hype from other quarters that I think is at best premature.
I think Go will be a fantastic alternative to C for "systems" programming, once it matures. I think this was one of its most immediate original goals, but really it is not yet ready to do even that. Two main reasons it is not ready for that is that performance is not yet there though I think it will get there; second reason is that it has no support for dynamic linking or code loading. You can't write something like Apache, or even Nagios in a platform that has no support for loadable modules. Still I think there are LOTS of applications where these constraints would not be an issue.
So then we have all this hype and people doing things like building web applications in Go and calling it the next Node.js or Ruby or Python and frankly this is just stupid. Despite some claims it has nowhere near the expressive power of those languages. It is statically typed but has no Generics - and there are quite a few use cases where there is no answer(https://groups.google.com/forum/#!topic/golang-nuts/PYJayE50...) and you will just have duplicate code.
It polymorphism but no inheritance - and the way interfaces and composition works while still fairly powerful is not at all what an OO programmer would expect(http://xampl.com/so/category/software/golang/) just naively reading about this feature. Whatever you want to say about the benefits of simplicity, this is LESS powerful than any OO dynamic language. Go's closures are nice, but aside from that on what basis can any other claims be made about its expressive power?
In fact, the language is intentionally very, very limited. They want large teams of mediocre programmers [1] to produce code that is very supportable. Everytime someone in the lists brings up a "why don't we have" question all the answers revolve around this premise, that the language becomes "too complicated" if you have crazy features like generics. So it seems Go is optimized to satisfy the angst of systems programmers who have to work on C code that is thirty years old and been maintained by dozens of people. I get it, but that doesn't mean its really a good language for hackers to use to build the latest social network for people who like cats.
Ok so concurrency. Yes Goroutines are smart. Channels are smart. These are great features. Scala can do the same thing (and more elegantly) with it's Actor constructs. The same thing can probably be done in other languages such as Ruby (https://github.com/igrigorik/agent). Its nice that this feature is in Go from the beginning, but its going to make its way into other platforms as well.
Performance. Well...lots of blog articles just keep repeating the mantra of "native code" as though it automatically means something. It doesn't. C isn't just fast because it generates native code, it is fast because it's compiler has been optimized for decades. Go doesn't have that today. In fact today, Go is slower than Java and Scala based on both Google's benchmarks (http://www.readwriteweb.com/hack/2011/06/cpp-go-java-scala-p...) and the language shootout (http://shootout.alioth.debian.org/u32/which-programming-lang...). Go will get there almost certainly, or at least very close, but it isn't there today.
Today if you want a static, fast language with good concurrency and an incredible standard library and extended library ecosystem then you want Scala I think. If you want a powerful, expressive language for quickly building web applications then you want Python, Ruby[2] or Node.js or similar. If you want a language is actually very fast and with incredible libraries you want C/C++.
I really do not hate Go. But it is not yet ready to replace hardly anything in my opinion.
edit: [1]You are right. The "mediocre programmer" comment is really out of line and a major distraction. I guess I actually think this IS a feature of Go: more maintainable code than C/C++ from mediocre developers. I'm bitter and tired ofdebugging twenty-year old C programs and I wish they were all written in Go. I shouldn't read that into Thompson's or Pike's intentions though.
[2]May brain stopped working here - I meant Ruby not Java.