Live data from Hacker News

Twelve Go Best Practices

talks.golang.org

61–70 of 153 posts

Re: Twelve Go Best Practices

#61

Earlier quoted context omitted.

> The file I/O makes the case for including exceptions in the language. Exceptions are included in the language, they are called panics, and the go convention is that libraries don't expose them in the public interface, but can use them internally (and, of course, application code can use them.) > Specifically, adding one-off types to deal with exceptions is a bug, not a feature. One-off types aren't used for error h…

My point is that an example of a 'best practice' shouldn't make the reader think: Oh, that's a kludge to get around a design decision in the language. In his example, he uses a one-off type to isolate the caller from having to explicitly check if each individual write failed. I got no problem with that. But it seems like it a work-a-round. It's just an odd choice for an example. The take-a-way seems to be that the ba…

> My point is that an example of a 'best practice' shouldn't make the reader think: Oh, that's a kludge to get around a design decision in the language.

I would think that some of the most important best practices would relate to the best means of dealing with situations where the approach users coming from other languages might naturally seek to apply are not the most appropriate, either because the other-language feature they are likely to have used does not exists (or works differently) or because of features in the target language that allow a better approach than in other languages.

> In his example, he uses a one-off type to isolate the caller from having to explicitly check if each individual write failed. I got no problem with that.

You'd do that if the library function threw exceptions, too. Eliminating repeated try/catch blocks (or calls to inline functions with deferred recover calls in go) and eliminating repeated if/then blocks are pretty much the same thing.

Re: Twelve Go Best Practices

#62

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…

W/r/t #2 - you're not familiar with Go but knew exactly what was going on. That's totally a feature. The language was designed around exactly that kind of reading. "break;" is implicit in Go.

Sure, I spend a decade in C. It's not hard to read.

My only problem with using generics in this context is that you can't catch type-conversion errors at compile time.

Seems like a step backwards with only downside. I get why exceptions are a double-edge sword. I'm not clear on why undermining compile time type safety is an feature.

Re: Twelve Go Best Practices

#63

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…

Quite a few people write deeply nested code for some reason unless you tell them not to. Maybe it depends on how your brain works. There is surprising variance within the human population.

I find deeply nested code ugly and unreadable, others nest eight levels deep and love it. Some people even claim they find parentheses soup (LISP-like syntax) readable. Personally, I cannot comprehend that.

Re: Twelve Go Best Practices

#64

Earlier quoted context omitted.

It's basic stuff because you're a Python weenie; I don't see much use or discussion of decorators outside the Python world.

Decorators are a special Python syntax for this use, the more general term for what is going on is "higher-order functions". Which are pretty important, and not specific to python.

yes, of course, but the discussion I replied to was talking about this specific idiom, this application of higher order functions, and not about the use of higher order functions generally.

Re: Twelve Go Best Practices

#65
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.

Re: Twelve Go Best Practices

#66
post #34

Holy shit that function adapters example is convoluted. I'd say fewer than 5% of my programmer coworkers would figure out what's going on. func init() { http.HandleFunc("/", errorHandler(betterHandler)) } func errorHandler(f func(http.ResponseWriter, *http.Request) error) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { err := f(w, r) if err != nil { http.Error(w, err.Error(), http.StatusInter…

It's important to compare with the previous slide, to see the problem this is trying to solve. It'd be really easy to do a lot of

  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
  }
all over the place, as the error return type isn't part of the standard Handler signature.

The decorator approach lets you return errors from your handlers, and then have your actual error handling centralized; you may want to send an e-mail to the ops team, send it to a third party exception management web service, etc.

Re: Twelve Go Best Practices

#67
post #32

Earlier quoted context omitted.

/pedant hat on Technically, the language does support exceptions. That said, they're in the "please never use this, ever." /pedant hat off The spirit of your comment is right, however -- the wonky code resulting from error handling, just like the "compile error on unused vars or imports," is something most new Go users find jarring.

> Technically, the language does support exceptions. That said, they're in the "please never use this, ever." No, its not. The convention is that any use of panics within libraries should be internal, and that libraries' exposed interfaces should use error returns. [1] Use of panics internal to libraries, or use of panics within application code that is not creating a library for others to consume, is not discouraged…

Thanks for the clarification, and the link :D

Re: Twelve Go Best Practices

#68

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).

Re: Twelve Go Best Practices

#69

Earlier quoted context omitted.

W/r/t #2 - you're not familiar with Go but knew exactly what was going on. That's totally a feature. The language was designed around exactly that kind of reading. "break;" is implicit in Go.

Sure, I spend a decade in C. It's not hard to read. My only problem with using generics in this context is that you can't catch type-conversion errors at compile time. Seems like a step backwards with only downside. I get why exceptions are a double-edge sword. I'm not clear on why undermining compile time type safety is an feature.

> I'm not clear on why undermining compile time type safety is an feature.

More safety usually requires a more complex type system. Such a type system is usually more expressive and can make more guarantees about your program, which is a pro. But of course, it is also more complex, which is a con.

Re: Twelve Go Best Practices

#70

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's being switched on the type of v (string or other, in this case), though in a more complex case you could easily have several different types. The assignment basically redefines v to be the matched type inside the case statement. You could easily just add a "sv := v.(string)" as the first statement of "case string", then use sv in place of v within that block, but this does read much cleaner.

I think it gets more interesting when using several, often complex (struct) types in the same switch statement.

Post reply on HN