Live data from Hacker News

Twelve Go Best Practices

talks.golang.org

111–120 of 153 posts

Re: Twelve Go Best Practices

#111
post #5

Didn't read, because mouse scroll wheel doesn't work. Honestly, who thinks this stuff is a good idea?

Thanks for the feedback, I just created a CL fixing it. https://codereview.appspot.com/11967047/

I think it's a good idea, btw :-)

You can run code on your slides, check http://talks.golang.org/2012/concurrency.slide#14

Re: Twelve Go Best Practices

#112
post #100

Earlier quoted context omitted.

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, t…

I don't see how the <- clues you in any better than the error handling that things might exit early. It seems from your response that the goto-ness of the go solution is not what you're really objecting to, because your proposal is equally goto-y. As far as I can tell, it's really the if-else verbosity that you don't like, which is fine (and I agree, exceptions are better), but it has nothing to do with the "whole point of structured programming." And, unless I'm mistaken, structured programming also lacked exceptions in its initial formulation.

Re: Twelve Go Best Practices

#113

Odd choice of examples... 1. The file I/O makes the case for including exceptions in the language. Specifically, adding one-off types to deal with exceptions is a bug, not a feature. There is a good case against exceptions but that ain't it. 2. On slide 5, it appears to show that you have to use a switch statement on a generic to get polymorphism because the language doesn't support overloading. Again, looks more lik…

Totally agreed about #1 and exceptions. In that defintion of DumpBinary, each bit of error handling takes three extra lines. Speaking of "cognitive load". Whereas here's what it'd probably look like in Python, with exception handling (assuming binary.write() raised IOError on error, which they should).

   class Gopher:
       def dump_binary(self, writer):
           """Write this Gopher to given writer, raise IOError on error."""
           binary.write(writer, binary.LittleEndian, len(self.name))
           writer.write(self.name)
           binary.write(writer, binary.LittleEndian, self.age)
           binary.write(writer, binary.LittleEndian, self.fur_color)
I can see the argument for Go-style explicit error handling, but having this as the first best-practice example just doesn't sit right or sell it very well.

Edit: Okay, I guess I should have read the next slide before commenting. Still, the "one-off utility type" is longer and more complex than the original error handling (so I wouldn't do it unless you're using it elsewhere as well).

Re: Twelve Go Best Practices

#114
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…

> Short circuit returns are the devil vi! Naïve, absolutist positions in areas of long-standing debate between programmers of great experience and the highest imaginable competence just makes you look ridiculous.

Naive, absolutist positions in areas of long-standing consensus between programmers of great experience and the highest imaginable competence makes one look even more ridiculous.

By and large, the best programmers eschew nesting in favor of early returns. Invariably (in my experience) those who argue against early returns are inferior programmers (and not only by virtue of lacking taste in this particular debate).

Re: Twelve Go Best Practices

#115
post #100

Earlier quoted context omitted.

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, t…

I don't see how the <- clues you in any better than the error handling that things might exit early. It seems from your response that the goto-ness of the go solution is not what you're really objecting to, because your proposal is equally goto-y. As far as I can tell, it's really the if-else verbosity that you don't like, which is fine (and I agree, exceptions are better), but it has nothing to do with the "whole po…

I think the main thing I'm objecting to is the possibility of multiple paths through the function. Doing it this way there's only one possible flow: a series of calls that pass blocks to validation objects (that then may or may not execute them).

Compare how smalltalk didn't have a conditional control flow statement. Rather, the boolean type has a polymorphic method that takes a block and then executes it or not.

The other nice thing about this approach is it makes the language simpler and more regular because it's implemented using standard language constructs rather than a special statement. E.g. you can write a "gather" function that takes a collection of monads and returns a monad of the collection, and this is generic in the monad (so the same function works to turn a List of Futures into a Future of a List, a List of Validations into a Validation of a List, etc.)

Re: Twelve Go Best Practices

#116
post #107

Earlier quoted context omitted.

> Short circuit returns are the devil No, they're not. > they make it much harder to factor out part of a function into a smaller function They generally make the function smaller, that's what guard clauses are for. > A function should have one entry point and one exit point That's absurd and makes for very ugly and unnecessary code.

> They generally make the function smaller, that's what guard clauses are for. My point is: frequently, one wants to extract a part from the middle of a function to make a new function. This is very easy (and can usually be done automatically) unless said part contains a return.

Suffering the unbearable burden of a single exit point just so your refactoring tool has an easier time breaking up the code isn't a trade worth making, especially when it's the single exit point that's probably making it too long to begin with.

Re: Twelve Go Best Practices

#117
post #92
post #84

Earlier quoted context omitted.

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 ||…

I've used a pattern such as this in my code [1]:

   	do := func(n int, err error) {
		if err != nil {
			panic(err)
		}
	}

        do(string.Write(/* 1 */))
        do(string.Write(/* 2 */))
        // ...
        do(string.Write(/* n */))
In fact, the pattern I've found is to declare throw-away lambdas [2] that I then call (few lines later). I haven't looked at all the pros/cons of doing this, but so far I find it's a very versatile way of doing.

[1]: https://github.com/aybabtme/graph/blob/master/digraph.go#L61 [2]: https://github.com/aybabtme/graph/blob/master/digraph.go#L10...

Re: Twelve Go Best Practices

#118

Earlier quoted context omitted.

> Short circuit returns are the devil vi! Naïve, absolutist positions in areas of long-standing debate between programmers of great experience and the highest imaginable competence just makes you look ridiculous.

Naive, absolutist positions in areas of long-standing consensus between programmers of great experience and the highest imaginable competence makes one look even more ridiculous. By and large, the best programmers eschew nesting in favor of early returns. Invariably (in my experience) those who argue against early returns are inferior programmers (and not only by virtue of lacking taste in this particular debate).

> By and large, the best programmers eschew nesting in favor of early returns.

Yup.

> Invariably (in my experience) those who argue against early returns are inferior programmers (and not only by virtue of lacking taste in this particular debate).

Yup.

Re: Twelve Go Best Practices

#119

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…

Because the alternative is worse. If you test-and-return after every call, your function has multiple exit points and is far less maintainable. If you nest like this, you at least have a chance of maintaining a single exit point in your function (even though this example fails to do so). This is why the Lord invented exceptions, which it seems that Go does not use. This one example is enough to convince me to never u…

I encounter this argument daily and I believe it usually comes from programming dogma of people who read very assertive quotes from Dijkstra.

Most of the time when error checking, returns (or in C, gotos) are fine and lead to more readable code that's easy to make sense of and easy to step through with a debugger.

    Please don't fall into the trap of believing that I am 
    terribly dogmatic about [the go to statement]. I have the 
    uncomfortable feeling that others are making a religion 
    out of it, as if the conceptual problems of programming 
    could be solved by a simple trick, by a simple form of 
    coding discipline!
- Dijkstra (1973) in personal communication to Donald Knuth , quoted in Knuth's "Structured Programming with go to Statements"

https://en.wikiquote.org/wiki/Edsger_W._Dijkstra

Re: Twelve Go Best Practices

#120

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…

People end up doing this because code gets written incrementally and often we start out wrong (e.g. here with the wrong/inverted condition). Rewriting large code blocks for a little more clarity is often a PITA. It would help very much if editors supported this better (e.g. single keystroke inversion of an "if"). Go with its easy syntax would be a particularly good target for automatic rewrites for code like the abov…

You can do that in Intellij Idea and related tools.
Post reply on HN