

Go Rocks - How Can We Avoid Something This Bad In The Future? - andrewcooke
http://acooke.org/cute/GoRocksHow0.html

======
supersillyus
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.

~~~
pgroves
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.

~~~
zem
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.

~~~
munificent
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?

~~~
uriel
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.

------
pcwalton
I work on Rust, which is in a similar space to Go. IMHO this article is
oversimplifying.

(1) Recursive anonymous functions: This is the first time I've heard this
criticism. MLs usually don't have this functionality either. It's not too much
trouble to give the function a name.

(2) Tail call optimization: TCO isn't free. It requires all callees to pop
their arguments, which slightly penalizes _every_ call in the program. Rust is
planned to support full TCO, but the current compiler only does sibling call
optimization (a subset of TCO).

(3) Continuations (I assume call/cc is meant here): Again, these are
expensive, because they interfere with the stack discipline. You end up having
to garbage collect stack frames in the worst case. For this reason both Go and
Rust have implemented exceptional control flow using stack unwinding and
destructors. (For Go panic/defer/recover play the role of destructors, while
for Rust we use a more traditional RAII system.)

~~~
a_strange_guy
(1) MLs allow to nest named functions. Go only allows named global functions,
and lacks a feature that Pascal had.

(2) Someone has to do cleanup anyhow. Either the caller or callee. (I am no
expert in calling conventions) Anyhow, isn't this practically free, because
the stack is in the L1 cache?

(3) To implement lightweight threads (ala. Erlang, Go, Stackless) you already
can't allocate your activation records on the C-stack. So the main cost for
call/cc is already paid for. Besides, Go lacks any form of non-local jumps, so
escape continuations alone (setjmp/longjmp) would already be an improvement.

~~~
koenigdavidmj
> Go only allows named global functions, and lacks a feature that Pascal had.
    
    
        func main() {
        	bar := func() {
        		fmt.Println("Hello, 世界")
        	}
        	bar()
        }
    

What do I lose by doing that versus having 'func bar()'?

~~~
a_strange_guy
this:

    
    
        func global(some_arg_to_close_over Bar) {
        	func walk_tree(node Node) {
                ...
                walk_tree(left(node))
                walk_tree(right(node))
        	}
        	walk_tree(something)
        }

------
singular
God what an annoying troll.

It's a very new language, and things are being improved + fixed all the time,
I would be very surprised indeed if tail-call optimisation + the ever so vital
self-recursive lambda issue weren't fixed at a later date. To treat these
things as permanently a part of the language is disingenuous at best.

Rob Pike (part of the core team) has said that what puts a lot of PL people
off is that it is _not_ theoretically exciting, it's just designed to be very
useful [1].

The funny thing about go is that it seems like an uninteresting language when
you first begin, but as you write more code you begin to appreciate its
simplicity and orthogonality and realise that these are extremely nice
qualities.

Language design is as much about saying no to things as it is to saying yes to
things.

On a related note, I don't like the implication that there is now a scientific
method to language design which obviates the need for practical considerations
- that strikes me as class A ivory tower bullshit.

[1]:[http://www.youtube.com/watch?v=-i0hat7pdpk&feature=playe...](http://www.youtube.com/watch?v=-i0hat7pdpk&feature=player_detailpage#t=2372s)

~~~
kraemate
I wonder what the author means by programming language "academic research".
None of the mentioned deficiencies of Go (anon lambdas/TCO) is the product of
any new academic research and have been around for a very long time (see
Lisp).

Most PL academic research goes into Haskell/ML , and we know how useful they
really are. (Monads/Type inferencing , anyone?)

------
dlsspy
I wrote a bunch of go code this morning. It's definitely got a few really neat
ideas in it.

I tend to write erlang or c++ mostly for my day job. I do really wish the
designers had a bit more erlang experience. Just a touch. Just enough so that
I had a way to propagate the death of a goroutine through a bunch of other
goroutines without building all of that manually.

For example, code I wrote today:

<http://pastebin.com/Zp6eqqTa>

That infinite loop is really infinite. If the goroutine feeding the channel
dies (e.g. if the connection is dropped), this thing will keep reading on the
channel. In erlango, I'd do something like "go_link client.Feed(ch)" and the
right thing would happen. Here, I really need to spin up a second channel to
send out the death spurt, select across the channels in my loop and send my
death message with defer.

defer is awesome, and I wish every language had it (C++ has something that
looks like it if you write enough code and squint hard enough). Still, I've
got a lot of code like the above and I imagine it's not an uncommon thing.

Every language has something that just feels unjustifiably wrong with it,
though. go certainly isn't one I'd be pained to use if I had to every day.

( __Edit __: couldn't figure out code formatting, tossed it over to pastebin)

~~~
rdtsc
> I do really wish the designers had a bit more erlang experience.

I feel the same way. I had this hope that this would be what I would learn
instead of Erlang. You know it has goroutines and channels -- kind of like
processes and casts. Well except that it is not the same. Erlang's processes
that have explicit pids and messages are cast to those processes by referring
to their pid, make a lot more sense to me than a bunch of channels. Maybe it
is just me, but I really just prefer the Erlang way. (Is the the "actor" vs
"CSP" paradigms?).

Then it is what you mentioned, exit signals. Anyone who wants to promote
concurrency as first order feature in the language and expects it to be useful
in the real world, would have to have something like supervision trees & OTP.
In other words when you have lots of little goroutines doing stuff
concurrently, it is expected some will just crash and then you'd want to do
something reasonable, which is not always to just crash the whole application
(OS process).

~~~
enneff
Go is something quite different to Erlang, despite some small similarities. If
you want Erlang, just use Erlang.

------
pyrhho
I really wanted to learn something from this post, but it's mostly just
hyperbole and whining.

"It turns out that, in Go, you can't write a self-recursive anonymous function
- there's no way to make it refer to itself. Well, you can do a dirty hack
that's the "official work-around" (I'll let you google for the bug report)."

To summarize this argument: 1\. It is impossible to have a self-recursive
anonymous function in Go. 2\. Oh wait.. It is completely possible, but far too
ugly to soil this blog post.

I looked up the temporary solution
(<http://code.google.com/p/go/issues/detail?id=226>). It is this:

    
    
        var recurse func()
        recurse = func() {
          recurse()
        }
    

The proposed _permanent_ solution is allowing recursive function definitions,
because that is the proper solution instead of adding a magic self keyword
like other languages do.

(tl;dr The author just doesn't like the fairly-simple work-around proposed
while the language is still developing)

Edit: There _definitely_ are far more important things in Go I dislike, and
would change, however this blog post is essentially fluff.

~~~
a_strange_guy
This is what they want:

    
    
        func outer(a int) {
            func inner_helper(b int) {
                ...
            }
        }
    

Pascal allowed nested functions 40 years ago...

~~~
Peaker
When saying "nested functions" in 2011, people expect you to mean lexically
scoped nested functions.

Pascal's nested functions really just scoped the name of the function
differently, but they were equivalent to a function definition on the outer-
most level.

------
fauigerzigerk
I don't think Go's creators are as ignorant of academic ideas as you say. I've
seen Rob Pike refer to OCaml a lot for instance. They just don't seem to like
many of those ideas.

~~~
gnuvince
I cannot recall him ever saying the word "OCaml".

~~~
fauigerzigerk
It's possible that I am mistaking Pike for someone else on the Go team.

~~~
dchest
Or the language? <http://en.wikipedia.org/wiki/Occam_(programming_language)>

------
cageface
Go's performance isn't good enough to broadly compete with C++ yet, and for
everything higher level I don't see a strong enough advantage over Java or
Scala to start all over with a new library ecosystem.

That said, I'm keeping an eye on it. If it takes off I'll learn it but it
doesn't make sense to make it a priority yet, IMO.

------
breckinloggins
I still think the major stumbling block for Go is the syntax. I can't put my
finger on it, but from the first time I saw it I thought it looked wrong.

Can anyone who shares my opinion chime in on what things they think cause the
syntax to suffer aesthetically?

~~~
enneff
I'm amazed by this particular criticism of Go. Compared to many modern
languages (Erlang or Ruby, for example), Go's syntax is barely different to
Java or C. But I think it's better than either of those (It's certainly more
regular).

Try it for two days and you'll change your mind.

Also, see: <http://blog.golang.org/2010/07/gos-declaration-syntax.html>

~~~
extension
It seems some programmers just will not accept new syntax at all. I think it's
a waste of time trying to please these people and, contrary to popular belief,
you don't need their blessing to make a successful new language. Coffeescript
is an example that supports this.

~~~
breckinloggins
It's a valid point, but it paints with too broad of a brush. For example, I
have enthusiastically moved from JavaScript to CoffeeScript, in part because I
love the syntax.

While I know there _are_ programmers who act just like you described, my
complaint is with Go's syntax specifically.

------
scscsc
I've played very little with GO, and it seems to be a nice language overall.

Anyway, it's designed to allow easy parallelism. It's a system's language for
parallel work.

The only thing that I don't like about it is that the built-in types get
special treatment (if you want to write a map yourself, you can't use the same
syntax). This indeed feels like a design smell.

GO is still very useful this way, but I do hope that it gets
generics/templates sometime in the future.

------
billmcneale
I think Go is a pretty mediocre language overall, one that would have been a
killer early 2000, but this article basically boils down to: "Go sucks because
it doesn't support tail call optimization and continuations".

Way to miss the boat.

~~~
xorglorb
I don't mind those as much, but what really kills it for me is the lack of
separation between the language and the standard library.

~~~
pavpanchekha
Wait, is there supposed to be such a separation? Python's standard library is
half the reason people use Python. Do I consider the MatLab matrix
multiplication routine the library or the language? Because the abstract
syntax and semantics of MatLab could certainly do without it. What about ODE
solving? I wouldn't use MatLab without it. Is Lisp's (defun) library or
language? In my own pylisp, I put (def) in the standard library. But you'll
see little code that doesn't use it.

What about Haskell? Is Prelude library or language? Every Haskell file has it
preloaded, so you can't code _without_ it (without effort, anyway); but it can
certainly be implemented in Haskell itself. Is Haskell the language just typed
lambda calculus with type classes and some basic syntax sugar? Is IO Haskell
language or library?

What about C's varargs? Is it language or library? It seems like library,
since you include a header file to use it. But it's also impossible to do
variable-lenght argument lists in C without it. What about longjmp? What about
sbrk? It seems like library, since different OSs do it differently, and thus
it has multiple implementations, in C. But without it, you can't do heap
allocated memory, which is something you can't do without it.

Separation between language and standard library? What language _does_ do it?

~~~
to3m
C is actually one of the few languages that gets the language/library
separation mostly right. You can write pretty much the entire standard library
in C, and you can easily write C without the standard library. It's very
straightforward (if sometimes a bit annoying) to do this in Windows, and I
believe neither EPOC/Symbian or PalmOS supported the ISO C library.

varargs, setjmp/longjmp and sbrk are 100% library. You can do varargs without
stdarg.h; if you know the calling convention, you can just walk through the
memory occupied by the arguments. setjmp is trickier but if you know the rules
you could write it using assembly language, just like the library writers did.
As for sbrk, I'm not terribly familiar with that I do admit, but judging by
the man page it is not part of the C standard library, kind of proving my
point. But you can definitely have a heap, and allocate out of it, and (where
plausible) grab more address space, without having it.

~~~
kragen
I agree with your broad point that you can write a usable C library with a
very minimal amount of assembly, but I am going to quibble on some of the
details.

> You can write pretty much the entire standard library in C, ... varargs,
> setjmp/longjmp and sbrk are 100% library.

If you're saying "These four functions can be written in C" then you are
mistaken. If you are saying "These four functions can be written in assembly"
then you are not saying anything interesting about C, because it is true of
_any_ language that it is possible to write its library in assembly if it's
possible to write it at all.

In more detail.

Whether you can write va_arg in C depends on the platform's calling
convention. On most platforms you can (because the default calling convention
has to support varargs, and the easiest way to do that is to push the
arguments on the stack) but on x86-64 you can't.

setjmp and longjmp cannot be written in C on any platform I'm familiar with,
unless you're writing them on top of something like setcontext, which has a
slight superset of their functionality. They are, however, very simple to
write in assembly.

You can write sbrk in one line of C, and the glibc implementation is only ten:
<http://koala.cs.pub.ro/lxr/glibc/misc/sbrk.c#L29> But sbrk is simply a
convenience wrapper around brk, which is necessarily a system call. And C
doesn't have a "system call" statement.

(Actually, you can write brk in C too if you just want to allocate out of some
fixed blob of address space, which is a reasonable choice on, say, a
microcontroller.)

------
jeffreymcmanus
He lost me when he said "to understand that, you need to understand the
academics".

There is a pretty sizable disconnect between what's going on in academic
computer science today and what professional software engineers are looking
for. Go explicitly sets out to solve practical problems, not provide an
implementation of this year's trendiest compiler porn.

------
simonw
For some reason I can't scroll this page on the iPad.

~~~
andrewflnr
Use two fingers. It's still a pain, but you can read it.

------
drivebyacct2
I know that this is the wrong mentality, but I want to become an expert in a
language that is as good as it can be. Can Go evolve to address some of these
issues? Would it even be go anymore?

I tried Googling, but why are Scala and Haskell automatically classified as
"academic"? Is there value in learning them purely for that reason? I walked
through some Haskell tutorials after reading this, but I'm rather stare at the
sun than read Scala.

~~~
flogic
I wouldn't call either Haskell or Scala academic. However, here a quick shoot
from the hip stereotyping of each.

Scala gives me the impression it wants to be Java's C++. C++ is largely an
extension of C. Sure you can use it entirely differently than C, but that
C-ish core is still there. And Scala gives me that same feeling in relation to
Java.

On the other hand, Haskell suffers from being a "pure" language. I mean "pure"
in a wider sense than "functional". I mean "pure" as in being built entirely
around a concept. For Haskell that concept is "functional programming". For
Lisp, it's the "cons cell". And for Forth, it's the "stack machine". The
benefit to being a "pure" language is there are all sorts of synergies you can
take advantage of. The down side is when something doesn't quite line up, it
gets ugly fast.

~~~
Peaker
I think Haskell accommodates imperative programming so well that it surpasses
many imperative languages and beats them in their own game.

I also think the name "purity" is misleading. In the context of Haskell it
means:

* Referential Transparency: references to things are indistinguishable from those things. This helps a lot with equational reasoning about code, optimizations and all sorts of mechanical refactorings. I don't think there are significant disadvantages here.

* Typed effects: Haskell types its effects and requires explicit composition of effects and not just of values. This, in turn, turns "side effects" simply into "effects" because they are so explicit. This has some disadvantages, mainly regarding learning curve. If you get a pure function type, you are _guaranteed_ many of useful things about its behavior. The flip side of this, is that if you want to change a pure function to have effects and break these guarantees, you will have to change its type and somewhat restructure it.

Haskell doesn't "get ugly fast" in any circumstance I've seen. It accomodates
an extremely wide array of paradigms as pretty encodings on top of its own
functional paradigm.

~~~
DanWaterworth
I completely agree, in fact I've been trying to show people how well Haskell
can encode imperative programs.

See
[https://github.com/DanielWaterworth/Musings/blob/master/why_...](https://github.com/DanielWaterworth/Musings/blob/master/why_haskell_is_suited_to_io.md)
and
[https://github.com/DanielWaterworth/Musings/blob/master/comp...](https://github.com/DanielWaterworth/Musings/blob/master/composable_database_logic.md)

