Live data from Hacker News

Twelve Go Best Practices

talks.golang.org

91–100 of 153 posts

Re: Twelve Go Best Practices

#91
post #83

Thirteen: don't try to sort, it's going to be painful if you do. http://golang.org/pkg/sort/ (See example 1 -- you have to write that for every concrete slice type you want to sort; it's not enough to write it once. And god help you if you also want to sort other collection types.)

Project Lombok for Go would be relative easy, and so nice.

Wow, that's magnificent, although I don't think Go's reflection capabilities are quite sufficient to fix this particular language bug with decorators. But maybe my imagination just isn't sufficient.

Re: Twelve Go Best Practices

#92
post #84
post #20

Earlier quoted context omitted.

The very next slide cleans it up using a "utility type" which reminds me of the "monadic" Haskell solution.

No way, the next slide is the very definition of evil unmaintainable code . Go through what is happening quickly: bw := &binWriter{w: w} bw.Write(int32(len(g.Name))) bw.Write([]byte(g.Name)) bw.Write(g.Age) bw.Write(g.FurColor) return bw.err If an error happens on L2, we will still run writes L3-L5. Why is this bad? Because in the future, we might come in and add logic after the writes complete. We have to make sure…

I would prefer to see something like (probably not legal Go, but would work in C, if err is of type bool):

   err = Write(int32(len(g.Name)))
   err |= Write([]byte(g.Name))
   err |= Write(g.Age)
   err |= Write(g.FurColor)
   return err;
If err is of type int with 0 == noError, this will require some new syntax if you want to return the correct error code (which you should want to do). I would suggest introducing

   x ||= y        x = x || y
In English: if x (still) equals its default, evaluate y and assign the result to x"

One could even shortcut this to

   err = Write(int32(len(g.Name)))
         || Write([]byte(g.Name))
         || Write(g.Age)
         || Write(g.FurColor)
   return err;
Advantage: that's how the shell works, too. Disadvantage is that 'logical or' is not what one associates with "run items until first failure", but that can be learned.

An alternative could be to have a language construct that takes a sequence of lambda's, executes them in sequence until the first one that fails (if any) and returns the index and the result code of the failing lambda. That would get you something like:

   index,err = SEQ([
           Write(int32(len(g.Name)))
           , Write([]byte(g.Name))
           , Write(g.Age)
           , Write(g.FurColor)
         ]);
I like the first idiom better, though.

Re: Twelve Go Best Practices

#93
post #10

"Deploy one-off utility types for simpler code" can be called a monad or Optional. I wonder if the language developers will add more formal support for that; it looks impossible to add Optional as a library due to lack of user-configurable generics.

Go occupies an interesting space. In my mind, I see it as competing simultaneously with C and Python. I suppose that the developers didn't see a place for an Optional type within that realm. I have to admit, I think it's a shame; huge proponent of non-nullability here. Especially given that with Go's lightweight lambda syntax a functor type would be easy to work with, I'm disappointed with its exclusion.

> Go occupies an interesting space. In my mind, I see it as competing simultaneously with C and Python. I suppose that the developers didn't see a place for an Optional type within that realm.

I would assume that everything that hasn't been implemented in Go 1.1 is not implemented because the devleopers of Go "didn't see a place for" it.

Now, either not seeing it as more important than the theings that did make it in, or seeing it as trickier to implement and acceptable to do without in 1.x, that's quite valid.

Re: Twelve Go Best Practices

#94
post #79
post #10

"Deploy one-off utility types for simpler code" can be called a monad or Optional. I wonder if the language developers will add more formal support for that; it looks impossible to add Optional as a library due to lack of user-configurable generics.

I'm sort of hoping someone forks Go to provide at least a few single-depth generics like Optional/Maybe, to match Map and Slice.

If it had been done at the very beginning, I think it would have been wonderful...but given that legal, idiomatic nil is already out in the wild I think it's too late. Such a shame - in the example of error handling, returning an Option doesn't appreciably increase verbosity:

    _, err := potentiallyErroringOperation()

    // with idiomatic nil
    if err != nil {...}

    // with monadic Option
    if err.isDefined() {....}
EDIT: fix typo

Re: Twelve Go Best Practices

#95
post #80

if err == nil { _, err := w.Write([]byte(g.Name)) if err == nil { err := binary.Write(w, binary.LittleEndian, g.Age) if err == nil { return binary.Write(w, binary.LittleEndian, g.FurColor) } return err } return err } Why does anyone have to tell people not to do this? How does it enter anyone's mind as a thing to do in the first place? I've been known to go too far to minimize nesting. I get twitchy at the second lev…

Short circuit returns are the devil - they make it much harder to factor out part of a function into a smaller function. A function should have one entry point and one exit point; that's the whole point of structured programming. If you're going to return from some random point in the middle of your function you might as well be using goto. (Of course, good programming languages provide a better solution than pyramid…

Having trouble picturing a better solution that isn't also morally equivalent to goto. Would you mind providing an example of your preferred approach?

Re: Twelve Go Best Practices

#96
post #79
post #10

"Deploy one-off utility types for simpler code" can be called a monad or Optional. I wonder if the language developers will add more formal support for that; it looks impossible to add Optional as a library due to lack of user-configurable generics.

I'm sort of hoping someone forks Go to provide at least a few single-depth generics like Optional/Maybe, to match Map and Slice.

Someone's already provided a template processor for/in Go that provides generics.

https://github.com/droundy/gotgo

Re: Twelve Go Best Practices

#97

Earlier quoted context omitted.

Go occupies an interesting space. In my mind, I see it as competing simultaneously with C and Python. I suppose that the developers didn't see a place for an Optional type within that realm. I have to admit, I think it's a shame; huge proponent of non-nullability here. Especially given that with Go's lightweight lambda syntax a functor type would be easy to work with, I'm disappointed with its exclusion.

> Go occupies an interesting space. In my mind, I see it as competing simultaneously with C and Python. I suppose that the developers didn't see a place for an Optional type within that realm. I would assume that everything that hasn't been implemented in Go 1.1 is not implemented because the devleopers of Go "didn't see a place for" it. Now, either not seeing it as more important than the theings that did make it in…

You make a valid point, but for something as fundamental as nullability, I think that's baked into the core language spec. It's possible that we could see an Option type in the future, but the fact that it's not an integral part of the language now means it would be unreliable and defeats the purpose of eliminating NPEs.

Re: Twelve Go Best Practices

#99

The type cast as part of the switch is really cool, I hadn't seen that before. switch v := v.(type) { case string: w.Write(int32(len(v))) w.Write([]byte(v)) default: w.err = binary.Write(w.w, binary.LittleEndian, v) } Great way to alter control flow based on the type, without a ton of ugly casts cluttering things up.

Of all the code in there this part confused me. What exactly is being switched on? It looks like v is being reassigned to the type of v, then the type of v is written out (instead of the value).

> It looks like v is being reassigned to the type of v,

v is not being reassigned, it is being declared (note := rather than =). But its a little complicated, because type switches are a special syntax construct that is similar to, but different than, regular expression switches. See, "Type switches" in the language reference [1]

[1] you'll have to scroll down a little within the section on Switch statements: http://golang.org/ref/spec#Switch_statements

Re: Twelve Go Best Practices

#100
post #80

Earlier quoted context omitted.

Short circuit returns are the devil - they make it much harder to factor out part of a function into a smaller function. A function should have one entry point and one exit point; that's the whole point of structured programming. If you're going to return from some random point in the middle of your function you might as well be using goto. (Of course, good programming languages provide a better solution than pyramid…

Having trouble picturing a better solution that isn't also morally equivalent to goto. Would you mind providing an example of your preferred approach?

Straight exceptions are also goto-like, but there's the slight improvement that they allow you to (automatically) refactor out the middle of a function without changing the behaviour.

The solution I prefer is a monad with some light notation (Haskell do, scala for/yield). So it looks like:

    def doSomething(): Validation[Whatever] =
      for {
        result1 
The Is this goto-like? You could argue so - once one computation fails, the rest turn into noops and we pass immediately to the end. But to my mind this is like replacing an if/else with polymorphism - our validation context is an object with certain behaviours, and we're calling methods on it with our functions as parameters, which will behave differently depending on which subtype our context instance is.
Post reply on HN