Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm not sure if yet another person comparing their set of agreement/disagreement is useful, but here goes, because I like Go and think some people are skeptical about it with a bit too much enthusiasm because it either seems too hip (this or that well known company or that adopting the stuff) or not-hip-enough (the research-PL inclined and even those who absolutely must have parametric data types).

Agreements:

All of the thumbs-up opinions I share, with the emphasis on that Go fills a niche that is otherwise a relative vacuum as-is.

The Damnable Use requirement, for precisely the reason he states. Add having to constantly frob a binding from "anySymbol" to "_" while doing some instrumentation or whatever. This kind of trivia should be fixable by a program, and otherwise let it remain a warning. I think the occassional positioning of this inconvenience as a feature is unconvincing.

Annoyance at the lack of assertions. I think the concern given by the Go FAQ can be met even without making everyone roll their own assertion construct, but now we have either fewer assertions or a less idiomatic way of identifying them. I think an assertion is quite distinct from an error in that it should indicate a logically impossible condition rather than an unusual one, and I often see this guideline applied in the world at large. As such, I feel the FAQ seems flimsy in its justification, and now I have panics littered about that have a convention indicating their special nature.

Assertions are also one of the very few pieces of software engineering practice that have even a smidgen of empirical evidence in their support as well: http://research.microsoft.com/apps/pubs/default.aspx?id=7029...

Leakiness of semicolon insertion. The abstractions are leaky, but I like not having to type my semicolons and it's not a common mistake I make. Minor.

Lack of clarity with regard to unicode. Yes, the language supports it quite well (as he mentions, unsurprising given its pedigree), but I'd say the type system and operators available only seem so-so, and it seems like more so than with Java or Python3 leaky abstractions between bytes and encoded text are present. Among my favorites:

http://golang.org/src/pkg/text/scanner/scanner.go#L521

Wherein the "rune" returned by the "text/scanner" "Scan()" is used to identify the type of token rather than its value. Sometimes. I was mystified by the type signature for a while (why is this returning a rune, and not a lexeme or lex type?) and had to read the package to convince myself that was the intent. Plus side: this kind of abstraction leakage (or at least free-wheeling coercion) can probably yield much faster code.

Disagreements:

"Violation space in time", whereby the semantics of a statement can be varied by the arity of the binding. I think this is good, and not bad. It's only a little weird that it's only available (afaik) as a special service to channel binding rather than any operator defined by a user.

Buffered Channels and Deadlocks: non-deterministic deadlocks are a pain, and I don't think Go provides any real treatment (or exacerbation) of this problem, however, it probably could make that possible by adding some operators to enable writing a run-time deadlock detector, which can be useful for some programs.

Notable omissions, speaking in the postive:

I think go fmt is great. I don't like everything about the formatting, but I like the slavery-is-freedom approach there.

The gdb support is surprisingly good and neatly integrated into most new gdbs one gets off the shelf. Massive thanks to Ian Lance Taylor.



Annoyance at the lack of assertions

In 10 years in the industry, building distributed systems and server-side software, I've never seen assertions work out well. In two of the jobs I was at, we ended up making the assertions always-on, even in production, simply because people were too scared to run with them off. At another job, we simply never used assertions.

I've come to the conclusion that if you think you need more assertions, what you really need is more unit tests. There are other benefits that come from designing for unit tests, like modularity. Assertions are just a crutch, and they're often a sign that you're not thinking things through clearly (if a is always false when b is true, why do I need two booleans? etc.)


I have found assertions very valuable in both Python and C. I have worked with code bases that both have them on in production and have them off in production, and the latter was generally the much more complex and more reliable system (it was also written with great care), but I do not think the on-ness or off-ness of the assertions was the principal cause of that (rather, the level of care/expense in writing the software). The are former were Python programs where the path of least resistance is to leave assertions on (AFAIK), and hitting assertion errors regularly has never been a problem. The latter is a proprietary fork of Postgres, which is peppered with highly useful assertions both by the open source variant and subsequently, keeping with a similar style, the labors of the contributors in the company later.

More unit tests are always nice, but practically, by having a cheaper and lower-overhead way to check invariant buried deep in the code enables more invariants overall. I see unit tests as a nice way to check invariants when it's possible to decouple assertions from the program, but they are generally more expensive to write and that means there will be less of them. Inverting a program to expose private state that has as many easy-to-express detailed invariants as one can think of often not practical.

The two approaches can be combined as well, by simply running with assertions on while also using unit tests.

I think assertions are most useful when evolving complex software to do new things or fix bugs. They're an also good, functional form of a comment. Practically they are much like an error (some performance sensitive systems can turn them off while in-production), so the Go approach to when-in-doubt-leave-it-out is reasonable, but I personally miss them because I still write assertions, they are now just my own idiom and otherwise look much too similar to actual error conditions.

One community I haven't visited in a while has been Java. Is it possible that assertions are more frequently abused there?


I think you are confusing together a few different things. A sanity check that is always on is not an assertion (at least by the usual sense of the word). Nobody is against sanity checks, just like nobody is against motherhood and apple pie. What I disagree with is the idea that sanity checks should be turned off in production, which is the core idea behind assertions.

It seems like your experiences confirm my own. People toy with the idea of turning off sanity checks in production, but eventually reality sets in and they realize that this is a bad idea. Then they keep calling the sanity checks "assertions," because nobody wants to submit a search-and-replace patch for a big code base, and a few die-hards still cling to the dream of running without error checking.

This leads to a lot of confusion whenever I talk about assertions (oh, assertions? You mean those things that are always on?) And so the cycle continues.


> It seems like your experiences confirm my own. People toy with the idea of turning off sanity checks in production, but eventually reality sets in and they realize that this is a bad idea. Then they keep calling the sanity checks "assertions," because nobody wants to submit a search-and-replace patch for a big code base, and a few die-hards still cling to the dream of running without error checking.

No. They don't. Practically nobody runs Postgres or the product we shipped with assertions on (it would be too expensive, and requires recompiling besides), and the only reason people run with assertions on in Python is because it's the easiest way to do things (the default), whereas in most C programs the default is to not have the assertions. However, I note that people don't seem to use assertions as crutch in Python in spite of them being on, because I very seldom see an assertion error crop up in production. When they do, I know the program has gone completely haywire and is no longer internally consistent rather than merely encountering an error it couldn't handle, and report the bug as such. This is very rare; practically, they could be turned off.

However, I get reasonable number of assertion violations when trying to change software while in development or while reading the source, and that's where I find them very useful.

I do reserve a special annoyance for slick assertions that are inadequately explained via comment.


So far we've established that: 1. you run with assertions always on in production, 2. you have encountered errors caught by the always-on assertions in production (it may be "seldom," but then all errors are seldom, hopefully.)

How does this not prove my point? Turning off error checking is stupid. Your software will never be bug-free. Start dealing with reality and rename your assertions to sanity checks, which is what they are and always have been.


> So far we've established that: 1. you run with assertions always on in production, 2. you have encountered errors caught by the always-on assertions in production

No. I gave two examples, one where assertions are run in production (mostly because it's easier) and I'm trying to communicate with you that I don't see people leaning on assertions to find bugs in production code very frequently at all, and this is the fear of the Go FAQ. I gave another example were assertions are invariably not compiled into the program when running in production. Am I not being clear about this?

> (it may be "seldom," but then all errors are seldom, hopefully.)

False. Errors (like "no permission to file", "out of disk", "out of memory") are distinct from 'an internal consistency check has failed.'

These happen all the time for completely legitimate reasons.

> Turning off error checking is stupid. Your software will never be bug-free. Start dealing with reality and rename your assertions to sanity checks, which is what they are and always have been.

Strong words. Okay, here's what I think is stupid:

* Software too slow for the purpose.

* Avoiding writing expensive invariants because it would make the software too slow.

* The notational similarity of a broken invariant that intended to never occur vs. an error condition.

My solution has been to accept the last-most option and to stop writing expensive invariants, even though I wish I could succinctly and idiomatically communicate to all maintainers that the invariant is considered impossible rather than merely an error condition and run with the expensive invariants while in development (a global variable and praying to the branch prediction gods can be close enough).


Needing lots of expensive invariants is almost "invariably" (see what I did there?) a code smell. You often see big, complex classes with 10 different booleans and a ton of assertion crap, when what is really needed is to refactor the class into smaller classes which are unit-testable. In C++, I've even seen people assert that an unsigned integer was >= 0. You can just feel the quality.

The notational similarity of a broken invariant that intended to never occur vs. an error condition.

Go has a notation for problems that are never supposed to happen: panic. For other errors, there are return codes.


> Needing lots of expensive invariants is almost "invariably" (see what I did there?) a code smell.

What's your definition of need? I want a lot of invariants if because I have found it reduces the chance of error. This is also the finding in that Microsoft case study I linked to. The more invariants, the better, and that means making them cheap to write.

> In C++, I've even seen people assert that an unsigned integer was >= 0. You can just feel the quality.

Entirely reasonable in some cases; it's telling me: "this software is not defined for negative numbers". There does exist code that can accept an unsigned number (because it is what callers found most convenient at the time, and casting is wordy) but is defined on negative inputs, and it would not be valid to put that assertion there. Those assertions are to assist future collaborators of your software, and shorter to write than:

    // This algorithm is only defined on positive integers
Until one is using a language with real dependent types, I think your specific example is not on the face of it as egregious as you say it is.

> Go has a notation for problems that are never supposed to happen: panic. For other errors, there are return codes.

Unfortunately I do see panic used -- even in the standard library -- as a flow control mechanism also. But yes, I do use panic for this reason, and tend to eschew using recover for convenience in most situations. It is the lack of an idiom to obtain complete clarity as to the nature of the panic that I find mildly irritating.


Asserting that unsigned numbers are >= 0 is a good idea... if you want to write code that ends up on thedailywtf.com.

I think we're done here.


OK, my response was maybe a little more snarky than I intended. Anyway, I think we are going to have to agree to disagree about the utility of turn off error checking in production.


Always-on assertions are a good thing. They help stop bugs before they become exploitable security vulnerabilities, for example. I would much rather have my web app crash on bad input than proceed to execute a malicious SQL query.


Assertions are a necessity in systems programming, especially when you're talking about OS kernels.

In one software project I know of, assertions are used to ensure that situations that should never happen never do.

This could be as something as harmless as incorrect (but harmless) usage of a device driver interface, or slightly more terrifying cases that could cause data loss if they occurred.

Before you claim that it should have more unit testing, it actually has mountains of it; again, the assertions are a safety net that prevents even worse things from happening without significantly impacting the performance of a production system.


>All of the thumbs-up opinions I share, with the emphasis on that Go fills a niche that is otherwise a relative vacuum as-is.

D, Rust, modern C++, ...


Disagree.

D is but one language (which does not, in and of itself does not fill a vacuum), and I have serious doubts about the social situation regarding its compilers. However, I think it meets most of the other criteria. It also has a lot of features (but not so ridiculously many as C++), however, I'd say it's closer to C++ than C in terms of language size.

Rust is brand new, pre 1.0, and I'm following it with great interest. I do not intend to use it until the low-level error handling has been fleshed out: https://mail.mozilla.org/pipermail/rust-dev/2012-March/00145...

Qualitatively, C++ feels closer to C than Go in many ways in terms of debugging and system aspects, and many C++ programs are written in the "just enough C++" style, which is very nearly C -- and of all possible evils, that is probably the least-evil one in my eyes.

Notably, C++ and D both have have try/catch/finally-style exceptions and inheritance, and while these features can be handy in specific situations, I think both are not desirable. These also apply to Python, a language I also basically like, but I prefer to not have them.


You're kidding right? Nothing stops you from writing any of these languages in a style like Go that doesn't use multiple inheritance and uses error codes. I suspect what's going unspoken is that you prefer others to code like you think instead of how they think. And Go gives you that because, at times above all arguments to the contrary, it strives to have only one way to do something at the language level.




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

Search: