So I tried Go the other day



  • @Kian said in So I tried Go the other day:

    @cartman82 won't deny that RAII was a bit broken before C++11, which caused a lot of issues. But the "bureaucracy" added nowadays is "write auto foo = std::make_unique<Foo>(); instead of Foo* foo = new Foo();".
    In exchange, you never have to write a "using" or "finally" clause to remember not to leak every other kind of resource. Never quite understood why people that like automatic management of memory like manual management of everything else.

    I am not a C++ guy, so I'll take your word for it. But you don't have to convince me, you have to convince people like Pike and Blow, who are actually designing new C-replacement languages :)


  • Banned

    @cartman82 said in So I tried Go the other day:

    @Gąska said in So I tried Go the other day:

    Whoever thought non-local error handling is problematic has smoked too much C/C++ crack. Their concerns have no place in memory managed languages.

    The problem isn't non-local error handling. The problem is not being able to tell which functions throw exceptions and which don't.

    I never found that to be a problem.

    "That" being random errors everywhere? Yes, they're usually not a problem, especially for user applications. But there are times where it is important to know what can fail and where and when and why.

    If I care about a certain type of error, I figure out what to expect and catch it.

    How do you figure out what to expect if you don't even know what can happen?

    Otherwise, I let it bubble to the general error handler at the app or request level.

    And if this happens often enough, eventually you're forced to face the bug ticket that says it happens. And then you have to track down why it happens, and what to do to make it not happen/what to do when it happens so the user doesn't get slapped with "something went wrong :(" as often as they're now. Alternatively, you could've made the program prepared for that right from the start - if only someone told you that this thing can indeed happen...

    It's very similar to static and dynamic type systems. Yes, dynamic types let you skip writing the obvious, and it's a huge save in character count. But then you end up with not exactly knowing what types of values a given function can return. You can either read the documentation and hope it's not outdated, or reverse-engineer library sources and hope it won't change in the future, or give up on life and just let all type errors bubble up to the general error handler at the app or request level. Or you could use static types and know exactly what to be prepared for and where.

    Figuring out what errors to expect can be an issue, forcing me to dig through the library source code or docs. But it's exactly the same type of problem with callback/return style error; you still need to figure out which error codes are possible and what to handle and what to pass through.

    The difference is, when you have to make it explicit that a function can raise error, then all the function that ARE NOT marked as raising errors are GUARANTEED to never raise any errors. This is arguably more important than knowing in what ways a failing function can fail.

    And those are a bit too verbose for most real world use cases and are shunned even by exception advocates, so...

    Yes, most people are lazy. This is one of the main reasons why abominations like PHP or JavaScript get so popular.



  • @Gąska said in So I tried Go the other day:

    How do you figure out what to expect if you don't even know what can happen?

    Blacklist instead of whitelist.

    I don't care about 99% of possible error conditions. The ones I care about, I'll research and handle. If I don't expect it, then send 500 to user and/or crash the app. I'll figure out what it was from logs and fix it if needed.

    Yes, I see how certain kinds of programs might need guarantees about execution flow, but that hasn't been the case with any of the programs I've worked on (mostly desktop, cli and web apps).


  • Banned

    @cartman82 I guess you're not a big fan of unit testing either.



  • @Gąska said in So I tried Go the other day:

    The difference is, when you have to make it explicit that a function can raise error, then all the function that ARE NOT marked as raising errors are GUARANTEED to never raise any errors. This is arguably more important than knowing in what ways a failing function can fail.

    The problem with listing exceptions is that the exceptions a function can throw is the union of the exceptions any function within it can throw. This may be tractable in simple examples, and leaf functions in your program, but it quickly grows ridiculous as your call graph grows deeper.

    The consequence is devs start trying to squash exceptions as soon as they are produced instead of letting them bubble to where it makes sense to deal with them. It's such a bad idea that exception specifications is one of two things C++ ever deprecated (off the top of my head, the other being auto_ptr).

    The opposite, marking when a function is guaranteed not to throw, is actually helpful and is the way C++ exception annotations evolved.


  • BINNED

    @cartman82 said in So I tried Go the other day:

    @topspin said in So I tried Go the other day:

    And C++ has had RAII from the beginning...

    That's basically adding a lot of bureaucracy to get what memory managed language does out of the box.

    That view certainly makes sense coming from the "memory managed language" side.

    I can understand why a lot of C gurus are boycotting the pattern.

    That doesn't, as you get the performance of the C equivalent with less code and a lot less potential for bugs.


  • BINNED

    @Kian said in So I tried Go the other day:

    The consequence is devs start trying to squash exceptions as soon as they are produced instead of letting them bubble to where it makes sense to deal with them. It's such a bad idea that exception specifications is one of two things C++ ever deprecated (off the top of my head, the other being auto_ptr).

    That's not the reason they were deprecated, as far as I understand it. They were just horribly broken and checked at runtime. Java exception specifications, as universally hated as they seem to be, don't have that problem.


  • Banned

    @Kian said in So I tried Go the other day:

    @Gąska said in So I tried Go the other day:

    The difference is, when you have to make it explicit that a function can raise error, then all the function that ARE NOT marked as raising errors are GUARANTEED to never raise any errors. This is arguably more important than knowing in what ways a failing function can fail.

    The problem with listing exceptions is that the exceptions a function can throw is the union of the exceptions any function within it can throw. This may be tractable in simple examples, and leaf functions in your program, but it quickly grows ridiculous as your call graph grows deeper.

    Or you can just put throws Exception to specify that you don't care about all that stuff and exceptions of all kinds can be thrown and user should be aware of that. If your exception specification has exceptions you don't care about, you're doing exception specification wrong.

    The consequence is devs start trying to squash exceptions as soon as they are produced instead of letting them bubble to where it makes sense to deal with them.

    There's no cure to laziness.

    It's such a bad idea that exception specifications is one of two things C++ ever deprecated (off the top of my head, the other being auto_ptr [not true but whatever -Gąska]).

    Well, yes, they deprecated it - but for a different reason altogether: since they were optional and not mandatory, their usefulness was close to zero. It wasn't a problem with the idea - it was a problem with implementation.



  • @topspin They were broken because there was no reasonable way of making them work. The cost of it being checked at runtime is secondary. It's not that C++ hates runtime costs, it just dislikes unnecessary runtime costs. "You don't pay for what you don't use" means you pay for what you do, not that everything has to be free. And it's not like the committee would be afraid of introducing difficult syntax if the goal could be met. But there's little practical reason to specify in the signature of the functions what it can throw. Breaking functions into "can throw" and "does not throw" is enough.

    Just for a quick rundown of why listing exceptions is bad, almost every function can throw "out of memory", or "illegal argument". Adding those to the signature of 90% of your functions is just noise. Similarly many other commons modes of failure, like trying to open a file that doesn't exist. From what I understand, Java solves that problem by creating a class of exceptions that are "unchecked", so they can be thrown without polluting the function signature. So even Java provides a way to cheat the system.

    So what is the point of checked exceptions? To ask the caller to please take care of that error? A function should declare what it needs to work, and the result of the work it does. It shouldn't mandate how the caller reacts to the result, because it creates a stronger binding between the caller and callee than there needs to be. If I don't handle the checked exception, then I have to pass it along to my parent, and they to theirs, and so on, and now my caller's implementation for some reason depends on who I call. If I swap my call with a different function that throws something else, now my caller's signature is changed even though neither one of us is interested in handling that error.



  • @Gąska said in So I tried Go the other day:

    Well, yes, they deprecated it - but for a different reason altogether: since they were optional and not mandatory, their usefulness was close to zero. It wasn't a problem with the idea - it was a problem with implementation.

    Doesn't the "throws Exception" make checked exceptions in Java optional as well?


  • Banned

    @Kian it also make the entire issue you have with checked exceptions go away, so...

    Edit: also, it's opt-out, not opt-in - and this changes the entire game. Also also, C++ functions with exception specifications can call exception-incompatible functions without catching - Java functions can't.


  • Fake News

    @Gąska said in So I tried Go the other day:

    @Kian said in So I tried Go the other day:

    @Gąska said in So I tried Go the other day:

    The difference is, when you have to make it explicit that a function can raise error, then all the function that ARE NOT marked as raising errors are GUARANTEED to never raise any errors. This is arguably more important than knowing in what ways a failing function can fail.

    The problem with listing exceptions is that the exceptions a function can throw is the union of the exceptions any function within it can throw. This may be tractable in simple examples, and leaf functions in your program, but it quickly grows ridiculous as your call graph grows deeper.

    Or you can just put throws Exception to specify that you don't care about all that stuff and exceptions of all kinds can be thrown and user should be aware of that. If your exception specification has exceptions you don't care about, you're doing exception specification wrong.

    Oh gawd no!

    Now every caller of your method needs either a try/catch or they need to declare their method as throws Exception as well.

    @Kian said in So I tried Go the other day:

    @Gąska said in So I tried Go the other day:

    Well, yes, they deprecated it - but for a different reason altogether: since they were optional and not mandatory, their usefulness was close to zero. It wasn't a problem with the idea - it was a problem with implementation.

    Doesn't the "throws Exception" make checked exceptions in Java optional as well?

    Not at all, it just means that anyone calling a method declared like that has no guarantee of what type of exception gets thrown, and hence they are forced to catch Exception (any type) or declare that they throw something they know nothing about. The throws Exception statement is only useful for test methods or somewhere you are sure that whoever is calling you will catch all exceptions anyway (and then e.g. log them without much further ado).

    If you really don't care you wrap any exceptions in a new unchecked RuntimeException and simply see your program crash.


  • Banned

    @JBert said in So I tried Go the other day:

    @Gąska said in So I tried Go the other day:

    @Kian said in So I tried Go the other day:

    @Gąska said in So I tried Go the other day:

    The difference is, when you have to make it explicit that a function can raise error, then all the function that ARE NOT marked as raising errors are GUARANTEED to never raise any errors. This is arguably more important than knowing in what ways a failing function can fail.

    The problem with listing exceptions is that the exceptions a function can throw is the union of the exceptions any function within it can throw. This may be tractable in simple examples, and leaf functions in your program, but it quickly grows ridiculous as your call graph grows deeper.

    Or you can just put throws Exception to specify that you don't care about all that stuff and exceptions of all kinds can be thrown and user should be aware of that. If your exception specification has exceptions you don't care about, you're doing exception specification wrong.

    Oh gawd no!

    Now every caller of your method needs either a try/catch or they need to declare their method as throws Exception as well.

    So?

    If you really don't care you wrap any exceptions in a new unchecked RuntimeException and simply see your program crash.

    Oh, right, forgot about unchecked exceptions. They make checked exceptions completely useless, as they allow non-throwing methods to still throw, putting us back at square one with what we know about code we write.



  • @JBert said in So I tried Go the other day:

    If you really don't care you wrap any exceptions in a new unchecked RuntimeException and simply see your program crash.

    The hallmark of an useful feature is that everyone has a different approach to how to avoid it.


  • Banned

    @Kian friendly reminder that when UAC first showed up, everyone was disabling it.


  • Discourse touched me in a no-no place

    You're all wrong. Use Brainf*ck. Solves all problems by making the programmer quit.


  • :belt_onion:

    The only correct error model (IMOIMIPTR) is this one described by Joe Duffy:

    Bugs Aren’t Recoverable Errors!

    A critical distinction we made early on is the difference between recoverable errors and bugs:

    • A recoverable error is usually the result of programmatic data validation. Some code has examined the state of the world and deemed the situation unacceptable for progress ...
    • A bug is a kind of error the programmer didn’t expect. Inputs weren’t validated correctly, logic was written wrong, or any host of problems have arisen. Such problems often aren’t even detected promptly; it takes a while until “secondary effects” are observed indirectly, at which point significant damage to the program’s state might have occurred ...

    Given that bugs are inherently not recoverable, we made no attempt to try. All bugs detected at runtime caused something called abandonment, which was Midori’s term for something otherwise known as “fail-fast” ...

    Exceptions thrown by a function became part of its signature, just as parameters and return values are ...

    void Foo() throws { /* ... */ }
    

    [Finally] a callsite needs to say try:

    int value = try Foo();
    

    This invokes the function Foo, propagates its error if one occurs, and assigns the return value to value otherwise.


  • kills Dumbledore

    @Kian said in So I tried Go the other day:

    The consequence is devs start trying to squash exceptions as soon as they are produced instead of letting them bubble to where it makes sense to deal with them

    Or, with checked exceptions, just sticking a throws Exception declaration on and ignoring the problem


  • Banned

    @CHUDbert said in So I tried Go the other day:

    You're all wrong. Use Brainf*ck. Solves all problems by making the programmer quit.

    Fun fact: Linus Torvalds, when asked why Linux is written in C, said this is the main reason.


  • Banned

    @svieira you did the equivalent of approaching people arguing whether Dodge or Chevrolet makes better cars, and telling them that the best car is the one with a working engine. Yes, you're correct - but you're also not addressing any points raised in the discussion, as both Dodges and Chevrolets have working engines - and you can find plenty of cars with broken engines from both brands too. Okay, maybe I was a bit too fast to call that. I thought the quoted part is the gist of the linked article, but it's actually a VERY LONG document (something like 50+ pages) that I'm still looking into. Most of it is still "both error codes and exceptions are heavily misused, let's focus on this", but maybe there's something actually insightful near the end.

    Edit: okay, so it looks like they went with checked exceptions and no unchecked exceptions. And they say it worked very well for them. And I believe in that, since it's a very good exception model. But they seem to have totally ignored the real reason why Java's checked exceptions are hated. Most programmers are lazy and clueless, and don't really care about errors. Their own team was made of world class experts, meaning they're very unrepresentative. If their idea became used in the wild, they'd simply end up with Java, but with try before every single method call.



  • Regarding checked exceptions and having a laundry list of exceptions your function can throw, that is pretty much the poster child for when you'd wrap it in a new exception of your own, turning all of them into a single one for the next guy to deal with.
    Checked exceptions are annoying because they are in your face about handling them, and unchecked exceptions blow your code up unexpectedly. If you're gonna work with exceptions, take your pick which is the most annoying and go with the other.



  • @svieira said in So I tried Go the other day:

    [Finally] a callsite needs to say try:
    int value = try Foo();

    This invokes the function Foo, propagates its error if one occurs, and assigns the return value to value otherwise.

    I know another language that does that. It's pretty nice, especially when you can also return values from the try/catch/finally in the catch/finally blocks.


  • Considered Harmful

    @Kian said in So I tried Go the other day:

    @JBert said in So I tried Go the other day:

    If you really don't care you wrap any exceptions in a new unchecked RuntimeException and simply see your program crash.

    The hallmark of an useful feature is that everyone has a different approach to how to avoid it.

    @SuppressWarnings("unchecked")
    public static <T extends Throwable> void rethrow(Throwable t) throws T {
        throw (T) t;
    }
    

  • Considered Harmful

    @svieira Welcome back!


  • Discourse touched me in a no-no place

    @JBert said in So I tried Go the other day:

    If you really don't care you wrap any exceptions in a new unchecked RuntimeException and simply see your program crash.

    That's basically the approach in C++ and C#, where making reliable software doesn't really seem to be a valued goal of the language. 🍹


  • Discourse touched me in a no-no place

    @pie_flavor said in So I tried Go the other day:

    @SuppressWarnings("unchecked")
    

    The hallmark of Shenanigans!


  • Fake News

    @dkf said in So I tried Go the other day:

    @JBert said in So I tried Go the other day:

    If you really don't care you wrap any exceptions in a new unchecked RuntimeException and simply see your program crash.

    That's basically the approach in C++ and C#, where making reliable software doesn't really seem to be a valued goal of the language. 🍹

    The trouble is that while the Java checked exception system is an interesting idea, it is so hard to come up with a clear rule of when a checked exception, unchecked exception, or error response should be used that not even the Java devs bothered, causing these two different worlds of people who use checked exceptions and those who curse every time they hit a method declaring it.

    Even though "multi-catch" got added late in the Java lifecycle, it seems unchecked exceptions have won when you look at all the functional stuff you now got since Java 8. None of the functional stuff can declare a throws without making a different interface, so almost nobody bothers.


  • 🚽 Regular

    @JBert said in So I tried Go the other day:

    error response

    Lately, and inspired by @pie_flavor">Rust's Result, I have been using something like:

    public class Result<T,E> {
        public T Result { get; }
        public E Error { get; }
        public static OkeyDokey(T res) { return new Result() { Result = res }; }
        public static Blimey(E err) { return new Result() { Error = err }; }
    }
    

  • Banned

    @JBert said in So I tried Go the other day:

    @dkf said in So I tried Go the other day:

    @JBert said in So I tried Go the other day:

    If you really don't care you wrap any exceptions in a new unchecked RuntimeException and simply see your program crash.

    That's basically the approach in C++ and C#, where making reliable software doesn't really seem to be a valued goal of the language. 🍹

    The trouble is that while the Java checked exception system is an interesting idea, it is so hard to come up with a clear rule of when a checked exception, unchecked exception, or error response should be used that not even the Java devs bothered, causing these two different worlds of people who use checked exceptions and those who curse every time they hit a method declaring it.

    The irony is, it's actually very easy to figure out. It's just that most people don't care about error conditions, so they do everything in their power to not have to deal with it. It's like doing proper multithreading vs. locking everything behind a shared global mutex.

    Even though "multi-catch" got added late in the Java lifecycle, it seems unchecked exceptions have won when you look at all the functional stuff you now got since Java 8. None of the functional stuff can declare a throws without making a different interface, so almost nobody bothers.

    Something tells me it wasn't a conscious design choice as much as sheer laziness.


  • Discourse touched me in a no-no place

    @Gąska said in So I tried Go the other day:

    @JBert said in So I tried Go the other day:

    Even though "multi-catch" got added late in the Java lifecycle, it seems unchecked exceptions have won when you look at all the functional stuff you now got since Java 8. None of the functional stuff can declare a throws without making a different interface, so almost nobody bothers.

    Something tells me it wasn't a conscious design choice as much as sheer laziness.

    It's one of these things where it gets complicated once you consider what happens when you parallelise. Forcing no failures there simplifies things quite a bit (since it means you've got to write down what you want to happen if a failure does occur).


  • Banned


  • Considered Harmful

    @Gąska I posted above what happens when you try to throw generically. It doesn't end well.


  • Banned

    @pie_flavor who said anything about throwing generically? I meant designing your functional streams to work both with and without possibility of errors.


  • Considered Harmful

    @Gąska you mean actually design the standard library around maneuverability? :doing_it_wrong:


  • Banned

    @pie_flavor don't get too far ahead of ourselves. For a start, I'd be just happy with designing around common sense.

    Yes, I do realize it's never going to happen either.


  • Considered Harmful

    @Gąska Yeah, we'll just say new ThrowingStream(new BaseStream(new StreamPipelineReference(list)), IOException.class). That's the Java Way™.

    I'm only half-joking - the current way, for things that haven't helpfully included a stream() method, is StreamSupport.stream(thing.spliterator(), false).


  • Banned

    @pie_flavor said in So I tried Go the other day:

    spliterator

    Oh my god this is real.


  • Considered Harmful

    @Gąska They literally have Stream.of methods for if you want to construct one out of a vararg, but constructing one out of literally anything else requires something much longer and an explicit spliterator. Whose fucking idea was any of this?


  • Discourse touched me in a no-no place

    @pie_flavor said in So I tried Go the other day:

    constructing one out of literally anything else requires something much longer and an explicit spliterator

    You can rely on the default stream() method implementation for Collection and Map most of the time.


  • Considered Harmful

    @dkf Yeah, except for when you want to start streaming an iterable that isn't a collection.


  • BINNED

    @Gąska What‘s the syntax for declaring “I’m throwing exception X plus all exceptions depending on generic parameters A, B, C”? Does any language have that?


  • Banned

    @topspin can you be more specific? I have trouble understanding what you're getting at. Would "I'm a wrapper around generic parameter A, and I can throw X as well as everything that A has declared as being able to throw" work for you? Because figuring out syntax for that isn't too hard.


  • BINNED

    @Gąska yes, exactly. Does e.g. Java have syntax for that? I know C++ has generic nothrow but not generic exception specifications.
    And of course inventing new syntax isn’t hard, that wasn’t what I’m asking.


  • Banned

    @topspin said in So I tried Go the other day:

    Does e.g. Java have syntax for that?

    Not that I'm aware of. Which just shows how little thought they've put in the design of their language and standard library (either due to incompetence or just laziness; alternatively, corporate overloads not allowing enough man hours to be spent on proper design - either way, Java sucks).

    I know C++ has generic nothrow but not generic exception specifications.

    Mostly because exception specifications are deprecated. And they've deprecated them because they haven't really thought it all the way through, and the result was something absolutely useless in practice.


  • BINNED

    @Gąska Yes, I’ve already commented on how they’re completely broken in C++.
    But it seems if you have both checked exceptions and generic programming you need generic exception specifications, that’s why I asked.


  • Banned

    @topspin "need" is a bit to strong. I'd rather say "necessary if you want it done right". Which very few people want, it seems.



  • @svieira interesting read, but it doesn't offer much of a defense for throw specifications. It points out that they avoid most of them by simply crashing at the first sign of a bug instead of throwing an exception.

    Which is a tough issue to handle. Clearly, if there's a bug and a value you are checking isn't in the range you expected, you can't know the state of the rest of your data. On the other hand, burning down the process to the ground may be fine in some situations but not necessarily always.

    If you're working on something that's part of a larger project where everyone understands that any process may die at any time, then sure, but that's not the state of most projects.


  • Fake News

    @Gąska said in So I tried Go the other day:

    either due to incompetence or just laziness; alternatively, corporate overloads not allowing enough man hours to be spent on proper design - either way, Java sucks

    It's a combination of things, but don't forget to also add "design by committee". There are at least 3 big JVM vendors which thus have to vet bytecode changes, similarly there are a few compiler vendors.

    If you like sausages or programming languages then you don't want to be present when they're made.



  • @Gąska said in So I tried Go the other day:

    @topspin "need" is a bit to strong. I'd rather say "necessary if you want it done right". Which very few people want, it seems.

    I got a bit lost, but then is there any language that does it right according to you?


  • Banned

    @Kian said in So I tried Go the other day:

    @Gąska said in So I tried Go the other day:

    @topspin "need" is a bit to strong. I'd rather say "necessary if you want it done right". Which very few people want, it seems.

    I got a bit lost, but then is there any language that does it right according to you?

    There's Rust, but it doesn't use exceptions, so I kept quiet about it. But it wouldn't be too hard to translate Rust's way of error handling into exceptions.

    Remember that until C# introduced var keyword and extension methods, no mainstream language had var keyword (or equivalent) or extension methods. Just because no one did something, it doesn't mean it's a bad idea.


Log in to reply