No syntactically lightweight way of writing anonymous recursive functions? I can't make myself care about that; I just spent 30 seconds trying, and failed. That's just not a use case worth optimizing for in a practical systems language.
Also, it's true that the Go spec doesn't guarantee tail calls are optimized. If Go were designed to be a functional language, that'd be problematic. However, guaranteeing TCO isn't free in implementation, and in the presence of features like "defer", it becomes non-trivial to understand when it is happening. So, while I like TCO as much as the next guy, I agree with the Go designers decision not to require it. Just like with a pile of other languages, the lack hasn't stopped people from writing good code, and not even stopped them from writing useful recursive code. I think there's some code in the Go standard library (parsing code, mostly) that uses trampolining to simulate it, and that code would be better off if it could just do a tail call, but on the balance that code still works well and isn't convoluted.
Anyway, not a bad blog post; I agree with most of it. But, as someone who has written a fair bit of code in functional languages and a fair bit of Go, I find that the more Go I write the less I care about the language features (or lack thereof) that I was horrified by at first, and the more I see most other languages as overcomplicated. Go isn't a very good language in theory, but it's a great language in practice, and practice is all I care about, so I like it quite a bit.
I'm a full time OCaml dev these days and I run into OCaml's 'lack of lightweight anonymous recursive functions' maybe three times a year. It costs 3 lines of code to declare the function (called loop() or whatever) and then call it. I've never realized I was supposed to want to switch languages because of it.
Otherwise, this comment makes me more interested in Go than I have been before. OCaml has a bazillion features that I never use, and since every OCaml program can look completely different by using those features, the libraries outside the standard lib are all but incompatible with my code.
what about pattern matching? that is imo one of the saddest omissions from go - it's not a "huge" feature like continuations, but i find it makes a language significantly more pleasant to work with.
I asked Rob Pike why they didn't add pattern-matching given that it already has multiple returns and some other baby steps in that direction. His response was, "We considered it, but it turned out to be too much work in the compiler."
That was a huge WTF to me. Isn't your job as a language designer to do that work so that I, the language user, don't have to?
The cost of complexity in the compiler is not only to the persons implementing it.
Also I'm pretty sure the amount of work in the compiler was only one of the considerations, and while I don't doubt your account of your conversation with rob, I doubt it was the main consideration in this design decision.
Pattern matching isn't just a feature to aid concurrent programming; it's extremely helpful for all sorts of code. However, since Go doesn't have unions (either tagged or unsafe), pattern matching in Go wouldn't be particularly powerful anyway.
I think you and a lot of other people got too caught up in the cognitively-well-trodden ground about how a language doesn't support anonymous functions and TCO very well, but missed some of the other less common arguments that I think are at least as important. The complaint about the compiler and runtime having too much magic access is very valid. I've come to see this as a major design flaw in any language, especially given how easy it is (relatively speaking) to give programs access to the magic interfaces instead of locking them away. Python is the best example of this, there is now almost no statement in the language that there isn't an official way to extend with some double-underscore protocol or other. It always causes problems when the language has some magical layer of access that automatically does something for you, because it eventually turns out to be the wrong automatic thing. It's only a matter of time.
And there isn't anything about that complaint that is the slightest bit "academic". If anything, the academics are far more into the idea that they can come up with the One Correct Behavior and write it straight into the underlying specification of the language than the practical languages are. (One of my personal criticisms with Haskell right now is that while this is slowly being fixed, it's being fixed in a haphazard, one-off for each syntax element manner, instead of seeing the underlying problem and addressing it in a unified manner, and I think that this is because, like I said, academics don't really think this way.) In my book, it's simply a mistake.
I think it's a grave mistake to collapse this criticism of Go to "It's not academic enough"; my sense of the problem is actually that it's more on the practical side. There are some clear practical wins that really ought to be well known by anybody doing PL design that seem to have been passed up. Hopefully these are corrected, but it dampens my enthusiasm somewhat that they were missed in the first place. YMWV.
Also, it's true that the Go spec doesn't guarantee tail calls are optimized. If Go were designed to be a functional language, that'd be problematic. However, guaranteeing TCO isn't free in implementation, and in the presence of features like "defer", it becomes non-trivial to understand when it is happening. So, while I like TCO as much as the next guy, I agree with the Go designers decision not to require it. Just like with a pile of other languages, the lack hasn't stopped people from writing good code, and not even stopped them from writing useful recursive code. I think there's some code in the Go standard library (parsing code, mostly) that uses trampolining to simulate it, and that code would be better off if it could just do a tail call, but on the balance that code still works well and isn't convoluted.
Anyway, not a bad blog post; I agree with most of it. But, as someone who has written a fair bit of code in functional languages and a fair bit of Go, I find that the more Go I write the less I care about the language features (or lack thereof) that I was horrified by at first, and the more I see most other languages as overcomplicated. Go isn't a very good language in theory, but it's a great language in practice, and practice is all I care about, so I like it quite a bit.