Hacker News new | past | comments | ask | show | jobs | submit login

>it leads to what I consider an abusive over-reliance on ad-hoc interfaces that just get in the way of understanding the code

I actually like it, from the architectural angle. Say, I have an entity (class, service etc.) which does only one thing X and it does it well. For a certain scenario, it wants also to do Y, and it wants to delegate it to a different entity, because it's not its responsibility. It doesn't really care how it's done and who will do it, it wants to just delegate it. Why should it know or care if there's an existing interface in some package? That's an implementation detail. The consumer specifies what it wants by declaring an ad hoc interface close to its own definition, and as a result, there's no explicit dependency on a different package. Sure, there will be duplication if several entities in different packages want similar interfaces but, as they say, duplication is cheaper than the wrong abstraction.

>in Go errors are just strings with a gross, inefficient library and system for adding context

Not quite true: errors are interfaces, there's a common pattern to construct an error from a string (because most of the time, that's all you need), but no one stops you from using other ways to construct an error. What is inefficient about it? It's no different from constructing an exception in Java/C# etc.

>Go's handling of dependencies...leaves something to be desired

Can you elaborate?




"duplication is cheaper than the wrong abstraction" is only true if the duplication: a) isn't also an abstraction (which, in go, it is in this case) and b) is "contained" (i.e. not overly used), which it often is not in non-trivial go code.

Go's errors package is riddled with `reflect` and other inefficient code constructs. Printing an error (e.g. to stdout or the log) is fine. But if you want to actually do anything with the error in the code (e.g. to discriminate/branch based on the error), you have to resort to bloated types that implement a poor interface or else rely on string inspection. In either case, you are using the underlying errors package. In small scale applications it's fine, but when you're doing anything at even modest scale and your application encounters errors, it's going to introduce measurable performance degradation.


>only true if the duplication: a) isn't also an abstraction (which, in go, it is in this case)

Can you give an example? I don't follow. I generally don't like abstractions for the sake of abstractions. I use Go's interfaces for a very specific reason: when I want dynamic dispatch, but with nice static typing guarantees. Interfaces in languages without structural typing force you to design a rigid, unflexible hierarchy/ontology well in advance, which only gets in your way when requirements change.

>is "contained" (i.e. not overly used)

Can you show why "containment" is necessary and why "overuse" is a bad thing?


We have a lot of Go services in production and errors have never been a performance issue. In the happy path, when there're no errors, errors are basically no-op. We don't use errors for control flow, though; only for exceptional situations, which aren't triggered often. If we want to branch based on an error, we use errors.Is. I don't remember ever having to inspect an error's string, that sounds like a hack. Usually, branching on an error's type is a rare scenario, even if it uses reflection, you usually just bubble up the error. In practice, at runtime, Go's error handling is just a bunch of TEST RAX, RAX instructions. Do you have benchmarks to show otherwise?




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: