
The Go Programming Language, or: Why all C-like languages except one suck. - jemeshsu
http://www.syntax-k.de/projekte/go-review
======
shin_lao
Too bad the points raised about C++ aren't valid. Ok, I admit, I get easily
upset by language bashing (C++ or another).

The problem is that when you notice something inaccurate in a document, you
have the tendency to ignore the rest...

 _It is a superior alternative for classic application development, and some
nice slim and full-featured GUI toolkits use its qualities well._

I would say C++ is the way to go for servers, not really GUI. As much as I
love C++ I wouldn't recommend using it for writing a GUI.

 _dynamic_cast <MyData_>(funky_iterator<MyData &const _>
(foo::iterator_type<MyData>(obj))_

I get it's a joke, but it would be a better joke if it was actually valid C++
or close to something you would actually write.

 _contemporary C++ using STL looks like a classic case of the "If all you have
is a hammer"-syndrome_

I don't understand what it means. The STL is a very powerful tool to implement
complex data processing and work on structure. Is this another case of someone
using the containers without using the algorithm's functions?

~~~
rayiner
Except that code isn't really that far from what you might type. I've spent a
lot of time working with code that uses templates heavily (including "well
designed" libraries like Boost) and it's just a god-awful mess. Make an error
in instantiating a template class and you get an error message that refers to
code deep in an STL header. Want to make your container class iterable? Get
read to write dozens of lines of inscrutable boiler-plate code. Even something
simple as iterating over a container is braindamaged. Before the auto keyword
you had:

    
    
        for(vector<some_really_complex_type>::iterator itr = something.begin(); itr != something.end(); ++itr)
    

Then you need to use "itr." It's a pointer-like thing that isn't quite a
pointer, and when you hold pointers in your container, which you often
(usually) want to do you have to deal with a pointer-to-a-pointer which is
almost never pleasant. And when you deal with keyed containers you have to
remember the iterator actually points to a pair so you have to do .first and
.second (why not .key and .value?)

Braindamage doesn't even begin to describe it.

~~~
shin_lao
I rarely use the "for" or "while" keywords. I use the algorithms instead.

The containers have been designed with this is mind.

If you don't use the algorithms, indeed, the STL can be cumbersome to use.

~~~
jamesaguilar
But then you have to define the loop internal logic elsewhere because C++ has
no lambda (yet, at least in most places where it's used in production). God
help you if you have a lot of variables that need to be compared or referenced
inside the loop.

~~~
shin_lao
Without lambdas you use functors.

~~~
jamesaguilar
> then you have to define the loop internal logic elsewhere

~~~
shin_lao
The loop is performed by the algorithms.

See std::for_each, std::transform, std::accumulate, std::partition, etc., etc.

------
johnfn
> So while C may be as lightweight as it can get, it's not really suitable for
> projects with more than 10k LOC.

What about the linux kernel? Or GCC? Both projects are on the order of
millions of lines of code. The author's claim is simply not true.

~~~
celoyd
Even though C is used for those large successful projects, even if it isn’t
_wrong_ for them, and even if the projects grew using C through a series of
justified decisions, C can still be suboptimal for them.

~~~
arethuza
There is no clear explanation of _why_ he thinks C is suboptimal - his
statement implies to me that there are other languages that are clearly better
for writing things like OS kernels etc. and I'm not that has been demonstrated
yet.

------
andolanra
Considering only pure language design, I have to say that I'd prefer D to Go.
A lot of people who talk about Go use some variation on the phrase "small sets
of orthogonal features"—a phrase I feel applies to Go only by comparison with,
say, C++—and D doesn't succeed in that regard, but I feel like D really fits a
lot of the points on the wish-list much more closely (e.g. template
metaprogramming, data structures, objects, &c. D's compile-time constructs are
incredibly useful without the nastiness of the C preprocessor or C++'s
templates.) One thing which draws me to D is the "you can, but you don't have
to" attitude it takes towards certain features—for example, there is GC by
default, but you can stop using it and do manual memory management if you feel
like it's important.

The problem here, and the massive, massive thing keeping me from throwing my
full recommendation behind it, is that D fails entirely on #7, because the
community is small and so even installing libraries by hand can be tedious. I
keep wanting to pull out D for personal projects, but then I come across some
obscure, poorly-documented library with few/no alternatives, and after trying
to build it for three hours, I give up and switch to something else. Recently,
'something else' has in fact been Go. I still feel like, in an ideal universe,
I'd rather program in D than Go, but we do not live in an ideal universe, and
of those two, Go is the practical choice. (And, despite my frustrations with
Go, it is still better by leaps and bounds than Java and C++.)

Also, quick correction: any dynamic language worth its salt does the same
short-circut evaluation with _and_ and _or_ , including Python, Ruby, Scheme,
and Common Lisp, so they all have the property ascribed in this writeup to
only JS and Perl. In Python, you can change whether instances of a class are
'true' or 'false' values by overloading the __nonzero__ method, which means
e.g. empty user-defined data structures could be considered 'false' while non-
empty ones could be 'true.' On the other hand, Ruby considers only false and
nil to be false values, Scheme considers only #f to be a false value, and
Common Lisp considers only nil to be a false value. Aside from individual
quibbles about which values are true and false, all of these languages
implement an _or_ that returns the first true value it finds, and all of them
implement an _and_ that returns the first false value it finds.

EDIT: Lua also allows the short-circuit boolean operators to return values.
The only widely-known dynamic language off the top of my head that doesn't do
this is Smalltalk. This would be complicated to add to a type system, for
relatively little gain, so as far as I know, no typed language allows it.

~~~
rsaarelm
Especially with Andrei Alexandrescu on board, D is striving for some very
interesting stuff with its template metaprogramming system. I think
Alexandrescu said in an interview somewhere that the goal is to have a
language where you don't ever need to reimplement an algorithm once you've
gotten it right once in a library.

This gets particularly interesting with mathematical code. If you have a
templatized math function like a linear interpolation function, you can swap
in integers, reals or complex numbers without writing new code, and also
matrices, vectors or quaternions from another library, provided that they have
the algebraic properties the function expects. Go is nowhere near allowing
this degree of write-the-algorithm-only-once, as it both lacks generic types
and has numeric types as privileged constructs you can't substitute with user-
defined ones.

~~~
andralex
Thanks for your kind words. In D's standard library we consistently attempt to
define each and every algorithm in its most general form, with opportunistic
specializations wherever available. As a trivial example, startsWith works on
average faster on sequences with O(1) length because it can compare the
lengths beforehand. D's generic amenities (static if and constrained generics
in particular) make it very easy to write code with lots of such micro-
specializations effortlessly. You just say "oh, do these guys support the
length method? Then if lhs.length < rhs.length return false".

We've managed to reach a very high leverage in std.algorithm
(<http://d-programming-language.org/phobos/std_algorithm.html>) because of
that, and there's seldom a need to redo by hand one of its algorithms for
efficiency or convenience reasons.

------
mycroftiv
This is a great article, although of course there are a few things to quibble
about. One that stuck out to me was this: "One of the inventors is Ken
Thompson of Unix and Plan9 fame, and he was indirectly involved with C as
well."

I'd have to say that Ken Thompson was directly involved with C, not just
indirectly!

~~~
guelo
Well, Ritchie is normally credited with the creation of C and Thompson with
its predecessor B. You're probably right, with the two coworkers working
closely together on Unix Thompson probably made a lot of direct contributions
to early C. But it was Ritchie that took C as his project and shepherded it
through its growth and standardization.

~~~
mycroftiv
Certainly Dennis Ritchie is the primary author of C, but given the very close
historical relationship between C and unix, we know that there was a tight
synergistic evolution that shaped each in relation to the other. As an
example, very early C didn't have structs, but Thompson clearly needed a bit
more powerful abstractions for some of the work on converting unix into C from
pdp assembler, so Ritchie added them.

I was really just quibbling over definitions and connotations, when I hear of
an "indirect" involvement I think of something very different and much more
remote than the deeply intertwined stories of unix and C and Thompson and
Ritchie at Bell Labs in the 69-74 era.

------
parenthesis
C is a C-like language. C++, being a superset of a language very similar to C,
is a C-like language. Objective-C, as a superset of C, is a C-like language.

But _Java_? and _Javascript_? They both have C-style _syntax_ , but apart from
that they are both very different from C (and from each other).

Please don't say `C-like' when mere `C-style syntax' is meant. (And please
don't think that having similar syntax implies any other close similarity
between languages.)

~~~
haldean
From a scope and features standpoint, C++ and Java are quite similar. I would
guess that that's why Java made the cut. No idea where Javascript came from,
though.

------
BarkMore
Here's a thread with comments by Russ Cox about the article:
[https://groups.google.com/d/topic/golang-
nuts/bg7U2tD04Fw/di...](https://groups.google.com/d/topic/golang-
nuts/bg7U2tD04Fw/discussion)

------
zvrba
Go is NOT C-like. The same semantics could have been achieved by making
minimal changes to the existing C syntax. For me, Go seems to be suffering
from the NIH syndrome -- they made many syntax and cosmetic changes to C just
for the sake of change itself. (Using {} for compound statements is not enough
to qualify the language as 'c-like'.)

I have no doubts that Go authors think that their syntax is superior, but
they'll have a hard time convincing me that

    
    
      switch nr, er := f.Read(buf[:]); true {
    

is understandable (snippet taken from Go tutorial).

~~~
chrisjsmith
It is perfectly understandable from my perspective, but the caveat is that I'm
used to Go's syntax and idioms and they are quite different from the usual. It
has taken a while to unlearn the old way to be honest but I prefer it like
this now.

~~~
zvrba
Can you explain the " ; true" part?

~~~
chrisjsmith
Straight from the tutorial:

"Since the switch value is just true, we could leave it off—as is also the
situation in a for statement, a missing value means true. In fact, such a
switch is a form of if-else chain. While we're here, it should be mentioned
that in switch statements each case has an implicit break."

The basic outcome is that:

1\. The assignment to er, nr is an initialization statement for the switch.

2\. The true (default value if not specified) is used to configure the switch
as an if-else chain which is required as the assignment above makes the
purpose of the switch ambiguous (is the result of the assignment configuring
the switch - how do you do that as multiple values are returned?).

You could rewrite it:

    
    
        nr, er := f.Read(buf[:]);
        switch true {
          ...
        }
    

or even:

    
    
        nr, er := f.Read(buf[:]);
        switch {
          ...
        }
    

But the switch initializer scopes it to the switch block cleanly.

~~~
zvrba
"switch true {...}" or "switch {...}"? really? So how would

    
    
      switch 3.141592654 { ... }
    

affect the case-statements inside?

~~~
supersillyus
When you say "switch <value>", it matches the "case" statements based on if
the value after "case" is equal to <value>. So, if you say

    
    
        const P = 3.141
        switch 3.141 {
           case P: 
              fmt.Println("This prints") 
        }
    

When no value is specified after "switch", the value of "true" is implied,
which is why you can do:

    
    
        switch {
           case a && b: ..
           case something():
        }

------
stephen_g
This seems like a fairly poor article overall... His point about GTK is
nonsensical - C is a very good language to use because it means that bindings
can be made for pretty much any language any language - which is why you can
use GTK in any language from C++, to Python, to C# and Java, PHP, Javascript
and so on... And C is used on thousands of projects more than 10K LOC, so I
don't see how it's 'not suitable'...

~~~
chrisjsmith
I think it's fair. GTK programming in C is painful (the moment you hit
gobject), which is where I think he is going.

~~~
gue5t
What part of it is painful? I've found gobject to be quite effective. It's not
the most enjoyable to extend (there's marshalling and other boilerplate that's
not difficult but not interesting either), but in the majority of cases when
using GTK you're doing just that--using it, rather than extending it. Using C
avoids the complex semantics of C++ templates/inheritance at the cost of
losing type safety when things become void*, but if you a have a little
discipline that tradeoff grants flexibility that's quite nice in my opinion.

~~~
chrisjsmith
That's about it - macros, boilerplate, constants. Straight from wikipedia the
ultimate yuck:

[http://upload.wikimedia.org/wikipedia/commons/1/17/GObject_e...](http://upload.wikimedia.org/wikipedia/commons/1/17/GObject_example.png)

I have no problem with C at all (I prefer it to C++).

------
natesm
The author says that he doesn't particularly care about speed if development
is nicer, but it's good to know anyways:

[http://shootout.alioth.debian.org/u32q/benchmark.php?test=al...](http://shootout.alioth.debian.org/u32q/benchmark.php?test=all&lang=go&lang2=gpp)

~~~
uriel
The Go x64 compiler (6g) has gotten more attention than the x86 one (8g), and
I think given that this days most systems are x64, this benchmarks page is
more representative:

[http://shootout.alioth.debian.org/u64/benchmark.php?test=all...](http://shootout.alioth.debian.org/u64/benchmark.php?test=all&lang=go)

~~~
igouy
Why do you think measurements made with the programs forced onto just one core
are more representative?

These are the x64 quad-core measurements -

<http://shootout.alioth.debian.org/u64q/compare.php?lang=go>

> _given that this days most systems are x64_

Given x86 and x64 and single-core and quad-core are all out there - the
benchmarks game shows measurements for each of them.

------
zitterbewegung
C isn't suitable for projects with more than 10k LOC? Ever hear about the
linux kernel? Or even libc?

~~~
jff
Both of which are... pretty terrible.

------
Johngibb
I'm wondering why they don't mention C#? Is mono non-viable at this point, and
he's only considering truly open source languages?

~~~
wladimir
He wants a language which can (preferably) run on everything from 8 bit to 64
bit CPUs and also run on various embedded OSes.

C# only covers a very small subset. Somewhat more if you include Mono, but I
guess that puts it in the same league as Java which he did mention.

~~~
jules
Does Go run on 8-bit CPUs? I highly doubt it, given that Go uses its own
runtime system including garbage collection.

~~~
dagw
Quoting Google's Russel Cox in a mailing list discussion linked to above: "Go
requires that int be at least 32 bits and that int64 and uint64 be supplied;
practically that means we've given up on 8- and 16-bit CPUs."

------
latch
I crossed a point in my life, I'm not sure exactly when, where reading c-style
code is just difficult for me. I see something like (from a Google sample):

    
    
       func (f Draft75Handler) ServeHTTP(w http.ResponseWriter, req *http.Request)
    

and at first I have an actual hard time parsing it, and then I think, why
can't this just be

    
    
       Draft75Handler.ServeHTTP(writer, request)
    

I partially regret this loss and partially rejoice in it. I'm sure it'd just
take a bit of practice to pick it up again.

Edit: I know why it can't look like that (because it can't be dynamic), but
its still what crosses my mind.

~~~
enneff
Some questions and observations from the other side of the fence:

How do you differentiate your simplified function declaration to a function
invocation?

The latter means you must refer to the receiver (the instance of
Draft75Handler) as "this" or "self". In Go, you name it explicitly (in this
case "f") which makes the code more readable IMO (although "f" is a strange
choice in this case).

The variable names "writer" and "request" will become wearying as you use them
in the function body - better to say the exact type once (http.ResponseWriter)
and use shorthand thereafter (w).

(And, obviously, omitting the type information in the function arguments
doesn't work in a statically typed language.)

~~~
ramchip
> (And, obviously, omitting the type information in the function arguments
> doesn't work in a statically typed language.)

It works just fine in Haskell for most cases.

------
sigzero
What the Go folks are trying to do is get traction. Without traction the Go
language won't be the "next big thing". So I expect we will see a lot of these
"types" of articles coming out.

~~~
supersillyus
You make it sound like a conspiracy. I get the impression that this is just a
blogger writing an article about a language he likes and some he likes less.
I'm sure the Go team and Go enthusiasts would like to see Go be more widely
known and used, but I get the impression that the Go team doesn't actually
want it to be the "next big thing". If they did, they'd have made more popular
design choices.

------
dkarl
I was disappointed not to see RAII on his list. I'd gladly leave C++ behind if
I could keep my RAII and the well-designed STL (a great idea and
implementation which is unfortunately uglified by C++-imposed verbosity.)
Actually, I'd happily leave even the STL behind, but I always miss RAII.

Rust supports RAII, but it might be premature to include Rust in this kind of
comparison.

~~~
zwieback
I agree. For me RAII is almost like a Litmus test. I can comfortably write
away in C# for a while but there's always a point in time where I want RAII
something and realize that it's something extremely useful you give up when
transitioning to a GC language.

The author did mention that GC has been around for C and C++ for ages yet
people don't seem to use it. If manual memory allocation was such a big
problem for C++ programmers people would have adopted a GC library long ago.

Which is not to say Go might not gain traction for other reasons but it's not
really clear to me what problem it solves, even after reading the otherwise
very entertaining and informative article.

~~~
misterbee
I would posit that if you choose to use C it is because you _want_ to
micromanage performance (kernel, strict hardware constraints, etc) and
therefore do not want GC, and if your environment is compatible with GC, you
may as well go further to C# or Java or another high-level language that gives
you even more goodies.

------
fauigerzigerk
_Python does foo if bar else baz, which is a little more verbose but still
okay. JS and Perl, however, rock with their boolean operators AND and OR not
just evaluating to true and false, but to the actual value that was considered
true._

Python does that as well:

    
    
      0 or False or 'Python rocks' or [] == 'Python rocks'

------
p0nce
> Well, actually there are semicolons, but they are discouraged. It works like
> JavaScript, there is a simple rule that makes the parser insert a semicolon
> at certain line ends.

I find it ironic that this "feature" is #1 in the list.

------
KirinDave
I generally liked this review, but I really had a hard time choking down the
sections on Concurrency (which was—charitably—poorly written and confusing)
and OO (which classically mis-defines OO).

It makes me wonder, why is concurrency really that much of a black art in
2011? I still see people confuse parallelism and concurrency and just the
other day an article got upvoted here describing why JavaScript programmers
don't need to learn about concurrency; as if the continuation-passing callback
style of JavaScript isn't a concurrency technique.

------
silon
Ceylon looks to be much better C-like language than Go. Go simply has too
divergent syntax.

------
jannes
I'm not a C++ programmer. Does anyone know how C++0x is coming along? Does it
address some of his issues with C-like languages?

~~~
hsmyers
I've not completely waded through the either the draft or the implementation
document, but this should help you:
<http://www2.research.att.com/~bs/C++0xFAQ.html>

------
daitangio
Some objections are questionable. The writer regret ObjectiveC for the lack of
a GC. Then blames Java for its size. Then he exalts GO for the GC. You can
find disavantages in every programming language, but are the advantages which
drive the choice.

You can also squeeze java a lot, running in less then 16MB. So have I miss the
point, or the writer is a GO-addicted?

------
itsnotvalid
I thought the ideal language he was talking about is Haskell.

------
briancray
I really like this comment regarding C-oid languages vs. scripting languages:
"Premature optimization is usually not worth it."

------
jasonjackson
tdlr: C < Go < Lisp

