{"action":"created","issue":{"url":"https://api.github.com/repos/andrewrk/zig/issues/122","repository_url":"https://api.github.com/repos/andrewrk/zig","labels_url":"https://api.github.com/repos/andrewrk/zig/issues/122/labels{/name}","comments_url":"https://api.github.com/repos/andrewrk/zig/issues/122/comments","events_url":"https://api.github.com/repos/andrewrk/zig/issues/122/events","html_url":"https://github.com/andrewrk/zig/issues/122","id":132869094,"number":122,"title":"slightly different error handling (long)","user":{"login":"PavelVozenilek","id":5585037,"avatar_url":"https://avatars.githubusercontent.com/u/5585037?v=3","gravatar_id":"","url":"https://api.github.com/users/PavelVozenilek","html_url":"https://github.com/PavelVozenilek","followers_url":"https://api.github.com/users/PavelVozenilek/followers","following_url":"https://api.github.com/users/PavelVozenilek/following{/other_user}","gists_url":"https://api.github.com/users/PavelVozenilek/gists{/gist_id}","starred_url":"https://api.github.com/users/PavelVozenilek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PavelVozenilek/subscriptions","organizations_url":"https://api.github.com/users/PavelVozenilek/orgs","repos_url":"https://api.github.com/users/PavelVozenilek/repos","events_url":"https://api.github.com/users/PavelVozenilek/events{/privacy}","received_events_url":"https://api.github.com/users/PavelVozenilek/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2016-02-11T01:46:54Z","updated_at":"2016-02-11T02:07:57Z","closed_at":null,"body":"Some time ago I proposed error handling quite similar to what zig does, for a different language.  I am copying it here as an inspiration. Syntax is zig-like.\r\n\r\n\r\n-----------------------\r\n\r\n(1) Errors are defined like in zig. They are just symbols with numeric value. 0 means no error\r\n\r\n-----------------------\r\n\r\n(2) Normal functions with no errors:\r\n\r\n    fn foo() { }\r\n    fn bar() -> i32 { return 1; }\r\n\r\nNo suprise here.\r\n\r\n-----------------------\r\n\r\n(3) Function which may return an error just do it, without need for any annotation or special type or anything. \r\n\r\nThe compiler knows that the function may also return error and this is important. Internally it would probably generate some union type.\r\n\r\n\r\n    fn foo() \r\n    {  \r\n      if (...) return MyError; \r\n    }\r\n\r\n    fn bar() -> i32 \r\n    { \r\n      if (...) \r\n        return 1; \r\n      else \r\n        return YourError; \r\n    }\r\n\r\nErrors can be easily added or removed from the function, one local change is enough. \r\n\r\n-----------------------\r\n(4) Returned error **must** be handled.\r\n\r\nThe compiler knows what called function may return and ensures that every error gets handled and only those which may happen are handled.\r\n\r\n    fn bar() \r\n    { \r\n      if (...) \r\n        return MyError; \r\n      }\r\n      if (...) {\r\n        return YourError; \r\n      }\r\n      ...\r\n    }\r\n\r\n    bar(); // error returning function invoked\r\n    //\r\n    // all possible errors must be handled now\r\n    // otherwise it won't compile\r\n    \r\n    // 'err' is pseudo variable imitating global errno. \r\n    // Actual implementation may be different. \r\n    // When function returns proper value 'err' is set to 0.\r\n    //\r\n    if (err) {  \r\n      // here we ignore all possible errors, \r\n      // it is something as catch (...) {}\r\n    }\r\n\r\n\r\n\r\n\r\n    bar(); // another call\r\n    // Here every possible error is handled explicitly.\r\n    //\r\n    // If I omit any error it won't compile.\r\n    //\r\n    // If I try to handle something what cannot happen it won't compile\r\n    //\r\n    if (err == MyError) {\r\n      ...\r\n    } elif (err == YourError) {\r\n      ...\r\n    }\r\n\r\n-----------------------\r\n\r\n(5) Regular return value cannot be used until errors are handled\r\n\r\nCompiler will ensure this.\r\n\r\n\r\n    fn foo() -> i32 {\r\n      if (...) {\r\n        return MyError;\r\n      }\r\n      if (...) {\r\n        return YourError;\r\n      }\r\n      return 1;\r\n    }\r\n\r\n\r\n    var x1 = foo();\r\n    // will NOT compile, error handling is missing, \r\n    // x1 may not be defined at this point\r\n    if (x1 > 0)  { ... }\r\n\r\n\r\n\r\n    var x2 = foo();\r\n    if (err) {\r\n      return SomeError;\r\n    }\r\n    x2 = x2 + 1; // OK, at this point all errors were handled\r\n\r\n\r\n\r\n    var x3 = foo();\r\n    if (err) {\r\n      x3 = 100; // I set value for the undefined x3\r\n    } \r\n    x3 = x3 + 1; // OK, at this point x3 is always well defined\r\n\r\n\r\n\r\n    var x4 = foo();\r\n    if (err) {\r\n       // empty body here\r\n    } \r\n    x4 = x4 + 1; // will NOT compile, x4 may still be undefined\r\n\r\n\r\n\r\n    var x5 = foo();\r\n    if (err) {\r\n       // here I CANNOT use x5\r\n       ...\r\n    } else {\r\n      // here x5 is valid\r\n      x5 = x5 + 1;\r\n    }\r\n\r\n\r\n-----------------------\r\n\r\n(6) Trying to do error handling where it is not needed stops compilation.\r\n\r\n    fn foo() { } // no errors\r\n\r\n    foo();\r\n    if (err) { } // will NOT compile, no error can happen at this point\r\n\r\n-----------------------\r\n\r\n(7) Errors can be passed up transparently.\r\n\r\n    fn foo() -> i32 {\r\n      if (...) {\r\n        return MyError;\r\n      }\r\n      if (...) {\r\n        return YourError;\r\n      }\r\n      return 1;\r\n    }\r\n\r\n\r\n    var x = foo();\r\n    if (err) { \r\n      return err; // pass any error up\r\n    }\r\n    if (...) { \r\n      return YetAnotherError;  // add own error\r\n    }\r\n\r\n-----------------------\r\n\r\n(8) When one is absolutely sure error cannot happen here and now it may be ignored explicitly.\r\n\r\n    var x = (no-err)foo();\r\n    x = x + 1; // OK\r\n\r\n\r\nThe compiler may use this as optimization hint. Will take the old ```foo```, remove all code related to errors and generate new, slim a fast alternative, and then invoke it from here.\r\n\r\n\r\nOnly certain error(s) may be known not to happen here. \r\n\r\n    var x = foo();\r\n    assert(err != MyError); // compiler understands semantics, will check MyError is corrent here\r\n    if (err == YourError)  { \r\n      // other errors still need to be handled\r\n      ... \r\n    }\r\n\r\nThis, too, may be used as optimization hint.\r\n\r\n\r\n-----------------------\r\n\r\n(9) Advanced error handling\r\n\r\nWhole block of code may be handled in one place.\r\n\r\n    {\r\n      // it feels as if no error handling is here\r\n      var x = foo() + bar();\r\n      x = x + 1;\r\n      x = baz(x);\r\n    }\r\n    // But here we go. When the first error occurs in the previous block \r\n    // it will jump right here. It is little bit like C++ exception.\r\n    if (err) {\r\n      // must handle foo, bar and baz errors\r\n      ....\r\n    }\r\n\r\n-----------------------\r\n\r\n(10) Even more advanced error handling. (I am not sure whether this is (easily) implementable.) \r\n\r\nPostponed error handling can possibly be extended to complete functions. They would look as if no error can happen but secretly would pass any that occurs up. All errors have to be caught, at worst in ```main()```.\r\n\r\nThis would be similar to effects in language Nim, or similar to checked exceptions in Java but w/o the need to annotate every function.\r\n\r\n    foo() { \r\n      if (...) return MyError;\r\n    }\r\n\r\n    bar() {\r\n      if (...) return YourError;\r\n      foo();\r\n    }\r\n  \r\n    baz() {\r\n      if (...) return OurError;\r\n      bar();\r\n    }\r\n\r\n\r\n    baz();\r\n    if (err) {\r\n      // handle foo, bar and baz errors\r\n      ...\r\n    } \r\n\r\n-----------------------\r\n\r\n(11)  When there are several function calls in one expression.\r\n\r\n    var x = foo() + bar();\r\n    if (err) {\r\n       // handle errors from both foo and bar\r\n    }\r\n\r\n\r\n    // Here execution will stop at the first error\r\n    foo().bar().baz();\r\n    if (err) {\r\n      // handle foo, bar and baz errors\r\n      ...\r\n    }\r\n\r\n\r\n-----------------------\r\n\r\n(12) What if single symbol for error is not enough?\r\n\r\nIf error needs to pass additional data it would be up to the programmer to implement it and to make sure it is all OK.  \r\n\r\nThis should keep compiler simple while still covering vast majority of use cases.\r\n\r\n-----------------------\r\n\r\n(13) What are the advantages of this approach?\r\n\r\n1. Simplicity. No funny annotations, no special types needed.\r\n2. Easier to refactor due to above.\r\n3. Compiler doesn't allow errors to disappear, doesn't allow unused code.\r\n4. The code looks as if written by very cautious C programmer."},"comment":{"url":"https://api.github.com/repos/andrewrk/zig/issues/comments/182672589","html_url":"https://github.com/andrewrk/zig/issues/122#issuecomment-182672589","issue_url":"https://api.github.com/repos/andrewrk/zig/issues/122","id":182672589,"user":{"login":"PavelVozenilek","id":5585037,"avatar_url":"https://avatars.githubusercontent.com/u/5585037?v=3","gravatar_id":"","url":"https://api.github.com/users/PavelVozenilek","html_url":"https://github.com/PavelVozenilek","followers_url":"https://api.github.com/users/PavelVozenilek/followers","following_url":"https://api.github.com/users/PavelVozenilek/following{/other_user}","gists_url":"https://api.github.com/users/PavelVozenilek/gists{/gist_id}","starred_url":"https://api.github.com/users/PavelVozenilek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PavelVozenilek/subscriptions","organizations_url":"https://api.github.com/users/PavelVozenilek/orgs","repos_url":"https://api.github.com/users/PavelVozenilek/repos","events_url":"https://api.github.com/users/PavelVozenilek/events{/privacy}","received_events_url":"https://api.github.com/users/PavelVozenilek/received_events","type":"User","site_admin":false},"created_at":"2016-02-11T02:07:57Z","updated_at":"2016-02-11T02:07:57Z","body":"@thejoshwolfe: (9) is like C++ exceptions. One needs to take lot of care not to leak or anything. I personally would be happier with handling after every call, but some may not. The (10) is even more ambitious. I put it in for completeness.\r\n\r\n\r\nThe actual errors may be listed by a hypothetical IDE. When compiler enforces their handling it should be easy. Code that is wrong (not handling/handling too much) will not compile. Reader should not be confused at any moment."}}