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?
"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"
[1] http://genius.cat-v.org/ken-thompson/interviews/unix-and-bey...
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.
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.
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].
[1]: http://robfig.github.com/revel/ [2]: http://harmful.cat-v.org/software/dynamic-linking/
Ken Thompson is known for many things. Catering to "mediocre programmers" is not one of them.
- "it has nowhere near the expressive power of those languages"
- "this is LESS powerful than any OO dynamic language"
- "there are quite a few use cases where there is no answer"(a link to a 100-post thread is not good enough)
- "Scala can do the same thing (and more elegantly)"
- "large teams of mediocre programmers"
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.
The lack of generics makes Go unsuitable to write typesafe,
reusable data structures for use in many popular
algorithms. That's a fact.
That is not a factual statement. It is subjective and it is disputable.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.
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.
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).
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.
... 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.
> Without dynamic linking it cannot replace C/C++ for
> lots of the intended use cases.
What use case requires dynamic linking?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.Here's one: writing a plugin for AviSynth in Go.
- Using a GPL-ed library in a BSD project.
- using a LGPL library in a commercial project.
- keeping source code secret from customers who buy your library.
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".
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.
It's unlikely to be addressed given that Google style guide forbids using default function parameters even in C++.
> This is a problem because it means lots of code will be written in which there are implicit defaults (just pass 0!)
"Just pass 0" is not a good way to implement defaults.
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)
}
Simple.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 Go, when you create a new variable, it's automatically set to the null/zero value. So in this case, they would be nil.
So not the same thing with default values at all.
With this struct interface you just implement "optional" values...
In skein package:
var DefaultHash = &Args{ ... } // the default args you want
Users will call it like this: skein.New(32, &skein.DefaultHash)
Another way, better for some cases, is have methods on arguments struct, like in base32 package from stdlib http://golang.org/pkg/encoding/base32/ base32.StdEncoding.DecodeToString(...)
base32.HexEncoding.DecodeToString(...)
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?
Because they do not share your belief that Go has "fundamental issues and well understood design issues".
If they did then your argument reductio ad absurdum would make sense. However, they don't; it does not.
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.
(1) them not believing that Go has design issues
for my argument to not make sense. It would need both (1) and
(2) them being right about said belief
for my argument to be invalid. And while (1) might hold, (2) is highly debatable.
And idiomatic Java code (Factory of proxies of singletons) is laughed at.
And not a lot of people are fond of idiomatic Ruby code, which tends to rely on "magic" (or "action at a distance").
Sometimes idiomatic is good, sometimes it's bad. Let's not cargo cult.
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.