Hacker Newsnew | past | comments | ask | show | jobs | submit | clausecker's commentslogin

Go had panic/recover right from the get go. Nobody “caved.” And indeed, you are free to use panic/recover for error handling in your code. That was always allowed.

That said, the key insight of the exception craze is that error returns are a normal part of a functions behaviour and as such should not use extraordinary control flow to take place.

Exceptions (panics) are used for things that should never happen or are indicative of a programmer error, like nil dereferences or out-of-bounds array accesses. That is, things where the programmer is not expected to provide reasonable behaviour on the API level if it happens (but perhaps there is a whole-program fault handler to shut down cleanly).

Opening a file that does not exist? Database record not found? Invalid credentials? These should be anticipated by the programmer and can occur at any time. Not exceptions, normal return values. Go requests that you think about these possibilities instead of pretending they can be ignored.


You're right about recover being in 1.0 of golang.

I definitely fully understand the trade-offs of returning result/error unions vs handling thrown exceptions. Exception handling is clearly superior to me. That said, the typical performance complaints made against the most common implementations of exceptions are valid.


> I definitely fully understand the trade-offs of returning result/error unions vs handling thrown exceptions. Exception handling is clearly superior to me.

Then you definitely don't understand the tradeoffs after all, and/or don't know the history of error handling.

Exceptions are an outdated concept that's inferior to explicit error returns for 99% of software.

The tradeoffs are super simple here - exceptions require very little of your input but lead to terrible spaghetti due to hidden control flow. Explicit error returns are seemingly the opposite, but their verbosity issues have mostly gone away thanks to modern language improvements, so they're just better, unless you're writing short scripts and the like.

The situation is very similar to static vs dynamic typing - dynamic typing used to make a lot of sense in the days of extremely verbose Java type declarations, but modern static type systems with inference have made dynamic typing basically obsolete.

> That said, the typical performance complaints made against the most common implementations of exceptions are valid.

This also sounds off and outdated to me. If anything, exceptions typically have better performance, so long as errors aren't common. Indeed, if optimized correctly, exceptions have zero cost if not thrown, which can get you better perf than error returns, which have a low, but constant cost.


Can you point me to a language that encodes the handling of errors in a less verbose way than C++/Java/C# exception handling?

Really bad idea.

Silently returning some (the wrong) value is always worse than catching the error right then and their and panicking. A panic is a noticeable symptom of something just having go wrong that is easy to chase after and debug. A silently returned wrong value just causes data corruption down the line, at great pain. It is very annoying to debug, in particular if people start to some times rely on this behaviour as an intentional part of their data flow.


I agree. I prefer Optionals over Nulls in any case. This was just a thought of what if Go went all the way instead of just halfway. In my ideal language, you would have explicit assignments and compiler checked (runtime checked if absolutely necessary) pointer validity (with allowed length) at construction. As far as I know, the only time you need to make a pointer and say "trust me" is when doing MMIO so that can given its own mechanism. If it exists in the program state, it's valid.


> No it really doesn't. It litters your code with if statements that are all just about the same, except that one that needs to be different, and you go blind looking at them all and can't spot the difference.

If most of your error handling is just bubbling the error up, you are doing something wrong.

  if err != nil { return err; }
is an antipattern. Not because it's verbose, but because you are supposed to handle the error, not pass it right through, in most cases.


You can do it like this, assuming A is the mask of newlines and B is the mask of non-spaces.

1. Compute M1 = ~A & ~B, which is the mask of all spaces that are not newlines 2. Compute M2 = M1 + (A << 1) + 1, which is the first non-space or newline after each newline and then additional bits behind each such newline. 3. Compute M3 = M2 & ~M1, which removes the junk bits, leaving only the first match in each section

Here is what it looks like:

    10010000 = A
    01100110 = B
    00001001 = M1 = ~A & ~B
    00101010 = M2 = M1 + (A << 1) + 1
    00100010 = M3 = M2 & ~M1
Note that this code treats newlines as non-spaces, meaning if a line comprises only spaces, the terminating NL character is returned. You can have it treat newlines as spaces (meaning a line of all spaces is not a match) by computing M4 = M3 & ~A.


Thanks for the suggestion. Indeed, that would be the first approach. That is how I started. This is however not considering the state (am I inside a 'statement' or not)

statement meaning string from first non-space till next EOL or EOF.

Problem starts when you need to cover the "corner cases". Without the corner cases the algo is not algo.


Do you mind trying out your solution? The code is in https://github.com/zokrezyl/yaal-cpp-poc Thanks a lot!

Obviously if your solutions gets closed to the memory bandwith limit, we will proudly mention it!


So I've thought about it and I don't really feel like spending more time to convince you that this works. If you have questions I am happy to answer them, but please write your own code.


It's fine and thank you! I am playing arround with the idea, in theory all is good.. Only thing is that things like "first non ..." often involve branching that corrupts the prediction ability of the CPU. Therefore I kindly invited you to show it in code.


You can find the first set bit in an integer with a machine instruction, it's completely branch free. gcc has __builtin_ctz() for this. You'll either need to iterate over all set bits (so one branch per set bit) or use a compression instruction (requiring AVX-512) to turn the bit set into a set of integers.

That said, as you seem to actually want to do something with the results, you'll take a branch per match anyway, so I don't see the problem.


This does track the state. If you want to track it across multple vectors of input, you'll need to carry it over manually.


We do this with FreeBSD ports, but users don't have to clone the ports tree unless they want to modify ports or compile them with custom options.


Yeah, using it as an actual source repository sounds fine to me. I think the issue comes when users are expected to clone it just for installation.


And the same reason NVRAM was dead on arrival. No affordable dev systems meant that only enterprise software supported it.


That's more "load store architecture" than RISC. And by that measure, S/360 could be considered a RISC.



FreeBSD


ARM already has most stuff required for this on board. Two proprietary extensions are used by Rosetta: one emulates the parity (rarely used) and half-carry (obsolete) flags, which can also be emulated conventionally. The other implementa TSO memory ordering, which can either be ignored or implemented with explicit barriers; some other chips apparently have a similar setting.

The other stuff is all present in ARMv8.5 I think.


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

Search: