

Things I Love About Go - fcambus
http://www.cambus.net/things-i-love-about-go/

======
koenigdavidmj
A lot of these are simply the Go people declaring certain conventions by fiat:
gofmt, the go build tool, a large standard library, and the like.

These are a lot of the benefits that Python had: everybody pretty much has to
do things the same way (and there's PEP 8 that establishes non-enforced
conventions), so I can start playing with a new codebase without having to
figure out their conventions and build system too.

~~~
drivebyacct2
It's unbelievable how helpful it is. The Go article that was posted last week
was lambasted for a lot of good reasons, but even as a Go fan I was annoyed at
how hard it was for me to quickly scan the source because it didn't follow the
conventions that I'm used to staring at quickly.

------
Hominem
There are some neat things about it. Being able to retroactively distill an
interface from code you do not have the source to. Sometimes I wish I could do
this in c# when I mock stuff up.

Goroutines and channels are cool because they are simple to use. We have tasks
in c# and I could construct channels in countless ways. But go makes them dirt
simple.

I don't like the terse c-like naming conventions but that is a manner of
taste. Makes it feel more close to the metal and less enterprisey I guess.

I wish there was go#

~~~
dsymonds
"less enterprisey" is a feature.

~~~
Hominem
Yeah I'm guessing that is why they went that direction. It really isn't any
less enterprisey than anything else and we ended up with stuff like
fmt.printf, who would have guessed printf is in something called fmt. Well I
guess it stands for format, but is printf primarily a formatting function? It
prints and the formatting is a convenience. If we are meant to use it for
formatting, it is badly named.

~~~
batista
Not really.

Printf is all about "formatted printing". What you think the "f" at the end
stands for and has stood for since C?

If anything, its the opposite of what you say, ie the "fmt" package name is
not measleading but redundant!

------
jbert
I'm learning a bit of go at the moment, and liking it a lot. One thing which
caused me a little head scratching was doing something like this:

    
    
        for _, item := range slice_of_obj {
            item.update()
        }
    

where the 'update' method wrote to some member data in item.

It seems that the 'item' is a copy of, rather than an alias to, the underlying
object, so the mutating 'update' call is updating the copy, which is then
discarded. (The 'right way' to do this to use the loop index and
slice_of_obj[i], but that seems mildly barbaric)

This seemed odd to me, particularly since no error was generated. Can anyone
comment if I'm doing anything particularly dumb here? If Go had a concept of
'const', at least it could throw an error when you mutate the copy which is
going to be discarded.

~~~
jgrahamc
The effect you are seeing there is because the as it says in the
documentation: "The iteration values are assigned to the respective iteration
variables as in an assignment statement." So item is being assigned and
because it's an obj and not a *obj you are getting a copy.

Whereas slice_of_obj[i] is accessing the element within the slice without a
copy. One solution to this problem would be to store pointers in your slice
instead of objects.

~~~
jbert
Thanks for the reply. I don't argue that it's not to spec, just that it seems
not useful/a bit surprising.

Some other languages (e.g. perl and I think also java) choose to make the loop
var an alias, rather than a copy, to allow mutation:

    
    
        for my $item (@items) {
            $item->frob; # Can happily mutate the item
        }
    

will work as it reads.

------
acomjean
Go seems like a rehash of Ada, without the option to dynamically linked. Maybe
an improved version of Ada.

I liked some feature of Ada a lot, it was safe, input, output, in/out
parameters for functions. Of course to make itself useful as a lower level
language you need to be able to call OS functions, which in unix means C. So
then you have to start wrapping... Some parts of Ada were terrible (string
handling comes to mind.)

I think its worth a look, but I'm still not sure what problem its trying to
solve.

~~~
Peaker
Go repeats the mistake of Boolean Blindness [1] so I think it disqualifies it
from "safe", though.

[1] [http://existentialtype.wordpress.com/2011/03/15/boolean-
blin...](http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/)

~~~
drivebyacct2
You posted this a few days ago, I didn't understand it and still don't
understand after another read through. Can you give either a dumbed down
summary or a scenario or an example of a language that doesn't do this?

~~~
Peaker
Basically, the idea is that when you branch on a conditional, information is
gained. This information may be represented in the type system and used by the
compiler to verify safety, or it can be ignored. If it is ignored, the
language is said to have "boolean blindness".

Example:

    
    
      if (ptr == NULL) {
        ... a ...
      } else {
        ... b ...
      }
    

In branch a and branch b, different invariants about ptr hold. But the
language/compiler are not verifying any of these invariants.

Instead, consider:

    
    
      data Maybe a = Nothing | Just a
    

This defines a type "Maybe", parameterized by a type variable "a", and it
defines two "data constructors" that can make a Maybe value: "Nothing" which
contains nothing, and "Just" which contains a value of type "a" inside it.

This is known as a "sum" type, because the possible values of the type are the
sum of all data constructor values.

We could still use this sum data-type in a boolean-blind way:

    
    
      if isJust mx then
        .. use fromJust mx .. -- UNSAFE!
      else
        .. can't use content of mx ..
    

However, using pattern-matching, we can use it in a safe way. Assume "mx" is
of type "Maybe a":

    
    
      case mx of
        Nothing -> ... There is no value of type "a" in our scope
        Just x -> ... "x" of type "a" is now available in our scope!
    

So when we branch on the two possible cases for the "mx" type, we gain new
type information that gets put into our scope.

"Maybe" is of course a simple example, but this is also applicable for any sum
type at all.

If your language does branching without giving you back any new type
information, it means you have to _manually_ track the invariants and
conditionals your program is in. If you get it wrong, your program will die at
runtime. Instead, the language can almost always provide you some kind of
proof that you can pass along of the conditional's truthness, which makes it
safe to rely on it.

~~~
zemo
I may be mistaken, but I believe type switches in Go address this criticism:

    
    
        type Fruit struct {
            Name    string
            Species string
        }
    
        // define a method on Fruit structs named Rename
        func (f *Fruit) Rename(s string) {
            f.Name = s
        }
    
        type Candy struct {
            Name    string
            Company string
        }
    
        // define a method on Candy structs named Rename
        func (c *Candy) Rename(s string) {
            c.Name = s
        }
    
        // any type that defines a method named Rename is also 
        // of type Renamable.
        type Renamable interface {
            Rename(string)
        }
    
        // produce something that implements Renamable.  Its   
        // fields will be unknown, but the method Rename will be 
        // known to exist.
        func SomeProducer(...) Renamable {
            ...
        }
    
        x := SomeProducer(...)
        // at this point, x is of type Renamable.  We do not 
        // know what fields it has.  The only thing we know is 
        // that it has some method named Rename that takes a 
        // single string for its only argument and returns 
        // nothing.
    
        switch t := x.(type) {
        case Fruit:
            // T is of type Fruit, while x is of type Renamable
            fmt.Print(t.Species)
        case Candy:
            // T is of type Candy, while x is of type Renamable
            fmt.Print(t.Company)
        default:
            // some other type.
        }
    

it's in the Switch section of the Go spec:
<http://golang.org/ref/spec#Switch_statements>

~~~
AnswerAndForget
Not exactly. You see, you have to define new types from scratch with your
approach. With Haskell's approach, you just have to use the Maybe parametric
type, which just needs to be defined once. In other words, you just go and use
a generic type instead of writing your own, like new List<Integer> instead of
new ListInteger in Java.

~~~
zemo
unless I'm missing something, that seems to address a separate difference in
the specifics of Go and Haskell's type systems, namely that Go's interfaces
and Haskell's sum types are different. Yes, they're different. I really only
meant to say that the article is predicated on the notion that we're only able
to branch on booleans instead of predicates. As for checking for null
values...

    
    
        var x *MyType
    
        ...
    
        switch x {
        case nil:
            // blah
        default:
            // blah
        }
    

tada, no boolean required, no having to "establish the provenance" of any
bits. Sure, inside the nil case, the compiler doesn't actually stop you from
unsafely attempting to access the fields of x, and, in that way, Haskell is
safer than Go.

I'm not trying to assert that Go is better than Haskell or that they are the
same; I've never programmed Haskell so I'm not qualified to make such a
statement. I'm merely addressing the specific "Boolean Blindness" criticism.

I don't really think that criticism is in any way valid, since "Boolean
Blindness" isn't a criticism of a language design, it's a criticism of how one
might use a particular language. To say that Go committed "Boolean Blindness"
is predicated on the following assumptions:

\- you can only branch on a boolean

\- given a value and a type, the only thing you can do is obtain a boolean
indicating whether or not that value is of that type

as far as Go is concerned, those statements are factually inaccurate.

Given some variable `x` of unknown type, and a type `t`, is there some way, in
Haskell, to create a boolean `v` such that the value of `v` is `true` when `x`
is of type `t` and `false` otherwise? And if so, is there some way to branch
on this boolean value `v`? Because if that is true, then how has Haskell not
committed the same atrocity of "Boolean Blindness"?

~~~
Peaker
> tada, no boolean required, no having to "establish the provenance" of any
> bits. Sure, inside the nil case, the compiler doesn't actually stop you from
> unsafely attempting to access the fields of x,

But you _do_ have to establish the provenance of your conditional. You have to
keep track of whether you compared against nil yourself.

Whether this is expressed as if(x != nil) or as a boolean-blind switch case,
the information is lost and it is up to the user to keep track of the meaning
of 'x' under all the conditions it is run.

Of course Haskell is capable of expressing boolean blind and unsafe code. But
Haskell, unlike go is also capable of expressing non-blind, safe code.

Go is _only_ able to express boolean blind code (again, it is _not_ about a
boolean condition but about whether branching buys you type information). When
you compare against nil, it is up to you to keep track of the provenance.

~~~
zemo
> You have to keep track of whether you compared against nil yourself.

that's true in the case of comparing against nil, which I've already agreed
with. I never said that Go is as safe as or safer than Haskell, I said that
your assertion that there's "no way to branch and get type information" is a
factual inaccuracy that misrepresents the language.

The fact that you haven't commented on type switches in any way shows that
you're not really interested in anything other than your own sense of
superiority.

Haskell is a better programming language. As a programmer of a programming
language that is not Haskell, I am inferior to you.

There.

~~~
Peaker
It's not just comparing against nil, it's any comparison or switch-case
besides a type-switch.

A type-switch seems to be non-boolean-blind indeed, but it seems to have too
much syntactic overhead, as Go code generally uses if branches when a type-
safe case is due (e.g: When analyzing return codes).

------
Nate75Sanders
_Statically linked binaries (No dependencies hell, easy to deploy in
production)_

Is this always this way? I'd be interested in a discussion on the pros/cons of
this. Some are obvious, but I'm interested in a deep discussion.

