
On Go - ishbits
http://dehora.net/journal/2013/06/01/on-go/
======
graue
The lack of exceptions just seems like a total bear to me. I wrote C for 10
years and did tons of stuff like:

    
    
        Open file "foobar.dat". If that fails, abort with this error.
        Read the first 4 bytes into x.
          If that fails, abort with a different error.
        Seek to byte x. If that fails, abort with yet another error.
    

and so on, and so on, over and over again. Python's exceptions are a huge
improvement over this pattern of timidly asking permission to do anything. The
fact is there are so, so many occasions where you want to abort a computation
if one of the many steps along the way goes wrong. Something like Haskell's
Maybe monad is a good way of attacking the problem too.

But Go has neither. It seems to just offer the bad old clumsy C way and say,
"Deal with it." To those who have written real Go programs, I'm honestly
wondering: how is this not a pain in the ass?

~~~
MetaCosm
Unlike C, Go isn't overloading what is returned, it has an extra param, and
the language has been baked to handle doing like initialization and checks in
single line if statements. It does force the developer who might actually have
a clue what the exception is and how to fix it to handle it, but IMHO this is
a good thing. Go forces lots of things like this (not using an import, won't
compile; not using a variable, won't compile).

Honestly, compared to the exception hellscapes I have had to deal with in Java
and C++ --- it seems like the path of least surprise. Which incidentally has
been my favorite things about Go, the low number of surprises.

A lot of using Go in real work has gone against my expectations. There are a
lot of things I initially saw as huge warts (lack of exceptions, generics and
import versions), but I liked channels enough (Erlang background) to give it a
shot. So far, I have been delighted by using it as a stack (build cycle,
deploy method, terse C'ish syntax).

~~~
throwit1979
The problem with this "forcing" that go does is that it ALSO includes _, which
means it's inevitable that lazy developers will get tired of handling error
and just shunt it into _.

~~~
nknighthb
You can't stop people from being lazy. Look at all the Java and Python code
that forcefully ignores exceptions.

It _is_ something that can be caught with static analysis, however. Someone
recently put together an appropriate tool[1] for Go, in fact. It seems to work
very well.

[1]<https://github.com/kisielk/errcheck>

~~~
pcwalton
To be fair, the ability to use static analysis for error code checking is not
something that is unique to Go. There was a paper recently on doing this for C
(which found hundreds of bugs in the Linux kernel due to incorrect error code
handling):

<http://pages.cs.wisc.edu/~liblit/ghc-2011/ghc-2011.pdf>

(Incidentally, the hard part of this analysis is not verifying that you
checked the error, it's verifying that you propagated the error codes
properly—that requires analyzing higher-order control flow.)

------
staunch
A tip from someone that just recently started using Go: read the spec! I
wasted some time early on trying to learn things that are very clearly and
simply spelled out in the spec. The Tour + Spec is really all you need if
you're a somewhat experienced programmer.

The spec is well written and tiny compared to most languages, and it's
probably the only thoroughly accurate and complete Go reference at the moment.

Step 1. <http://tour.golang.org/> Step 2. <http://golang.org/ref/spec>

~~~
elithrar
Effective Go (<http://golang.org/doc/effective_go.html>) and Go by Example
(<https://gobyexample.com/>) are also great. The former is especially so if
you want to learn Go idioms and understand why things are done. It's a good
complement to the spec.

~~~
MetaCosm
For some reason, I really enjoy showing this slideshow to people ->
[http://blog.menfin.info/Presentations/20120709_Golang_introd...](http://blog.menfin.info/Presentations/20120709_Golang_introduction)
... it is short, gets across a lot of the core ideas very quickly.

------
taliesinb
I love how the dialogue on HN about Go has gone from pessimism and largely
uninformed criticism, to regurgitation of the team's own talking points
["simple, orthogonal features"], to a more nuanced appreciation of what trade-
offs Go makes.

Hopefully these are individual developers shifting through a continuum of
enlightenment, rather than the conversation itself migrating to a more
enlightened population.

This last question is testable, of course, though sadly HN does not offer an
official API.

~~~
Peaker
Those of us who think that Go is a truly sad language to release at modern
times (nullability everywhere, no parameteric polymorphism, no sums, products
for errors instead of sums, ...) have just lost interest in explaining over
and over again why a new language without those features belongs in the 1970's
or 1980's, and not modern times.

The crowd that remains is mostly composed of people who have never used an ML-
style language (Haskell, OCaml, F#, SML, ..) and come from a background of C,
Java and Python. Go is definitely an improvement over these languages in many
areas.

~~~
MetaCosm
As a pragmatist and someone who enjoys Haskell and F#, I find your comments
very academic. There will always be less popular, feature packed fun languages
like Rust, Haskell and OCaml, and there will always be teams that use them to
great advantage (Jane Street). But IMHO, lots of these systems are a little
creaky in the support structures (cabal for example) and deploy features.

That said, I think a lot of what makes Go great is because of its simplicity,
lack of surprises, and general lack of cleverness. You can get your hands
around the language features very easily, in mere hours.

Beyond that, I think the ease of building tooling on top of the AST (or in
general), the ease of deploying code to production, the build speeds, the
inclusion of go get and fmt, the policies around imports and variable use (use
or lose) all add up to be more than the sum of its parts. It is very obviously
built by a team looking to use it in production, on real projects, as soon as
possible.

~~~
Peaker
> But IMHO, lots of these systems are a little creaky in the support
> structures (cabal for example) and deploy features

Cabal used to be horrendous. Now it's just mediocre, and better than the
distribution tools I've seen in the Python world (e.g: easy_install). Much
better than the lack of distribution tools in the C and C++ world.

> That said, I think a lot of what makes Go great is because of its
> simplicity, lack of surprises, and general lack of cleverness. You can get
> your hands around the language features very easily, in mere hours

nil everywhere is an easy to explain language feature. It might be easier to
explain than pattern matching. It's definitely worse though.

I'm not saying that Go has no strong points in its favor.

It has plenty surrounding it that you mention.

It's just sad that this is all around such a poor language.

~~~
MetaCosm
The horror of python dependencies, build and deploy (I currently am working on
a 250k+ LOC python project) is part of what made me really love what is around
the edges of Go.

There is a lot of "worse" in Go. It seems the "worse" choices were for 3
reasons: (1) make the language simpler, and (2) make the compiler / tooling
simpler to build or (3) ship a production usable product in X time. I think
all might yield benefits in the long term.

Getting angry about a "poor language" is pointless, there are hundreds. It
seems people get more angry that it might get popular more than their favorite
language. For a bit, I was a bit of a stick in the mud about Go because I was
hoping Erlang (maybe with Elixir) would take off. After that I was worried it
might get popular before Rust had a chance to get off the ground.

But, after having spent some time with it -- I have grown to like its odd,
pragmatic mix of tooling and features.

~~~
Peaker
I didn't say I was angry. I said a new language that repeats past mistakes
_again_ is sad.

The authors of Eiffel went ahead and paid a very dear price to fix their
nullability-everywhere. The C# author says if he could go back in time and fix
one design mistake in the language, it would be the nullability-everywhere
mistake. That it is responsible for a huge percentage of field issues with C#.

Yet Go was designed after these, and still put in nullability everywhere.

Seeing society pour tonnes of resources into a bad language, when we could all
have benefited from these resources being poured into a good one is sad.
Having a new language with new approaches and ideas is great. But one that we
already know will create poorer quality software, not so great.

What a huge waste of talent and resources we're seeing here.

P.S: Cabal is not really so bad anymore.

My experience when using "cabal install <pkgname>" is generally 90% success,
and 9% failures that I can fix by a simple "cabal unpack" on some overly
restrictive package to fix its version constraints and carry on.

~~~
burntsushi
Fixing the "null" issue isn't straight-forward. If you eliminate it
completely, then you need to add sum types to the language. If you provide
nullable pointers in addition to non-nullable pointers, then you're adding
complexity to the language by having two different kinds of pointers. It's a
trade off. The Go designers chose a different side of that trade off than you
would have. That's perfectly reasonable.

I don't think you appreciate arguments for pragmatism. I write plenty of C and
Go code. I rarely run into null pointer errors in Go, while I run into plenty
of them in C. It's possible to mitigate the Billion Dollar Mistake without
encoding it into the type system.

This null issue has come up plenty of times on the Go mailing list. Search for
the billion dollar mistake, and you should find some responses from the Go
devs.

> But one that we already know will create poorer quality software

We do? I think you meant, "I believe it will create ...".

~~~
tikhonj
Adding sum types to the language is not a bad thing. In fact, it's a great
thing, even outside of nullability! It would also make error handling much
neater--and not a special case--in Go. Sum types also have a natural symmetry
with structs, and in language design (just like in physics), I figure symmetry
means you're on the right track.

Of course, to make both of these reasonable, you would also have to add
parametric polymorphism. And while you're at it, you may as well throw in full
type inference. (I mean, why not?)

These aren't gigantic changes, and it's already well-understood how to
implement all these efficiently--OCaml has it all, along with a fast compiler
that outputs fast code.

But Go doesn't. I really wish Go had taken more (i.e. _some_ ) inspiration
from OCaml.

Also, ignoring the merits of this particular case, I don't agree that a
decision is reasonable just because it's the "different side" of a tradeoff.
Virtually _any_ choices can be recast as tradeoffs, but there are still wrong
decisions to be made!

~~~
burntsushi
I know all about sum types. I love them. My central point was to show that
removing nullability is a trade off rather than a freebie. I thought the
context of the discussion made that clear. I feel that you did not address my
point other than to say, "well yeah, but it's wrong."

> These aren't gigantic changes

Then we have a fundamental disagreement. Adding sum types and parametric
polymorphism would drastically change the language. I am _not_ claiming that
the change would be for better or worse.

> Also, ignoring the merits of this particular case, I don't agree that a
> decision is reasonable just because it's the "different side" of a tradeoff.
> Virtually any choices can be recast as tradeoffs, but there are still wrong
> decisions to be made!

Could you point me to authoritative sources that state how languages should be
designed?

Going by the character of your post, it seems like you don't care much for the
cohesion of a language design. The Go devs care about this, _a lot_. So saying
"we should add feature X, and while we're at it, Y too" without stating how
those features will integrate with the rest of the language just isn't going
to cut it for people who care about the totality of a language design.

------
kodablah
My biggest issues with Go are compatibilities with the C ABI and the lack of
shared library support. I have not looked in a while, so I may be wrong, but
here are my big questions: 1. Can C invoke a Go-compiled and exported
callback/function? 2. Can I build a Go .so/.dll invokable via C or other non-
Go FFI methods? 3. If I build a commercial Go lib for others to link with, how
can I distribute it without distributing the Go source?

~~~
MetaCosm
I suspect unless someone gets real exciting about this and modifies one of the
toolchains, this won't happen in the near future. You can do (1) with some
ugly syntax. Regarding (2) and (3) -- I know some projects are happening in
both those spaces, but haven't kept up with them.

~~~
zellyn
As far as I know, you can call from C(++) into go, as long as the outermost
layer (main) is go.

------
hcarvalhoalves
Everybody compares Go and Python. How is it so? Go compiles to a binary,
implements static typing, got rid of classes, got rid of exceptions, and added
lots of special purpose keywords on top of reinventing C's syntax. The only
similarity I see is the "import" statement.

~~~
jlgreco
They both approximately match up on mental effort /friction/hassle for me,
which largely seems to be coincidence since they _do_ have dissimilar feature
sets. For example, it use to be that I would use Python when I thought that C
(my language of choice) was going to be too much of a hassle; now I use Go
when I think that C will be too much hassle.

I think this accounts for a large portion of the Go<->Python talk you see.

~~~
fauigerzigerk
Keyword arguments with default values is the one Python feature that makes my
Go code more verbose than my Python code.

    
    
      def f(a=11, b="some default", c=55.5, d=D()):
        #...
    
      f(c=44.4)
    

becomes

    
    
      type Config struct {
        a int
        b string
        c float
        d interface{}
      }
    
      func f(c *Config) {
        a := 11
        if c.a != 0 {
          a = c.a
        }
        b := "some default"
        if c.b != "" {
          b := c.b
        }
        c : = 55.5
        if c.c != 0.0 {
          c := c.c
        }
        d := new(D)
        if c.d != nil {
          d := c.d
        }
        //...
      }
    
      func main() {
        f(&Config{c: 44.4})
      }

~~~
nknighthb
I just solved a similar problem in a slower but fairly general way. It's not
appropriate everywhere, but it works well for the use-case I built it for.

I can't share the actual code, unfortunately, but by using struct field tags,
reflect, and json, you can end up with code that looks something like this:

    
    
        type FArgs struct {
            A int `def:"11"`
            B string `def:"\"some default\""`
        }
        func f(margs map[string]interface{}) {
            var args FArgs
            SetupArgs(margs, &args)
            // ...
        }
    

If f() is being called in a tight loop, your overhead can be enormous, but
outside performance-critical code, it's probably good enough.

~~~
fauigerzigerk
You're not just losing performance but also static type checking.

~~~
nknighthb
Not really. If the passed-in or default value doesn't convert to the field's
type, SetupArgs panics.

Edit: Variations on this that offer compile-time type checking of passed-in
values are possible, too. My use case wouldn't work well with that, though,
because the function needs to be called by other functions that would have no
clue about the FArgs struct.

You could instead do a version that just swaps in a default for any nil
fields.

And in any case, you've got a lot more safety than the original Python code,
no?

~~~
fauigerzigerk
_"And in any case, you've got a lot more safety than the original Python code,
no?"_

Only insofar as your approach puts the values back into a struct that can
subsequently be used in a type safe manner. Arguably that's better than having
no type safety at all.

But I think my main gripe with your design is that it is not just less type
safe and slower, it also comes with more mental friction and verbosity than
what Python does.

Granted, it's a lot less verbose than my Go code, which makes it a good
solution in some scenarios. But in my view it's not a good general replacement
for default keyword args and you didn't claim it was.

~~~
nknighthb
I certainly wasn't arguing it was a perfect substitute for default args, just
that it was easier to manage than the example you gave.

The application I'm currently working on is mostly a direct port of Python
code to Go. Python code I originally wrote. You needn't tell me it fails to
accomplish all the things Python's default args do.

------
501
I (a go noob) somehow got the impression that you're supposed to version your
API when writing go libraries. ie, your library should be
github.com/501/foo/v1 rather than github.com/501/foo. Can any go users comment
on whether that's expected practice?

~~~
nknighthb
I suspect you got this erroneous impression due branches based on the version
of Go they are compatible with.

Many projects maintain branches named for the Go version they are compatibile
with, and the 'go get' tool automatically fetches the appropriate branch.

~~~
501
Actually I think I may have first picked it up from this vclock library:
<http://labix.org/vclock>

------
rvirding
Erlang does automatically parallelize over multiple cores. By default it will
start one Erlang VM thread per core which work together to run the Erlang
system. The Erlang VM also does automatic load balancing over the threads and
even tries to shut down threads/cores if it detects they are not needed. It is
possible to control how many Erlang VM threads you want at start-up and to
lock them to cores as well, though the latter is not recommended. But as I
said there is no need to do this.

