proj-oot-ootErrorNotes2

Difference between revision 30 and current revision

No diff available.

"Also, for Rust 2, I'd suggest exceptions. The "new error handling model"[1] is excessively verbose. Repackaging low-level errors for the next level up requires considerable coding. I understand the big problem with exceptions is what to do about unsafe code. I'd suggest "just say no". Unsafe blocks cannot raise exceptions. If an exception in safe code unwinds into an "unsafe" block, it becomes a panic. Unsafe code in Rust is usually to interface with non-Rust code. If you have unsafe sections in pure Rust code, you're probably doing it wrong.

The secret of exception sanity is a standard exception hierarchy, like Python's. Catching an exception class in Python also gets all its subclasses. "EnvironmentError?" covers all I/O errors, network errors, and such, but doesn't cover internal program errors. New user-defined exceptions are added below the most appropriate part of the standard exception hierarchy. So Python usually doesn't have Java's exception hell. If exceptions go into Rust, do it that way. Require that all exception objects descend from some predefined exception object in the standard exception hierarchy.

An additional advantage of a standard exception hierarchy is that it leads to straightforward internationalization. If all the standard exceptions have translations, users get semi-reasonable error messages in their own language. If you have too many string-type error messages in a program, it's hard to handle multiple languages.

Go is starting to get exceptions through the back door. People are abusing the "panic"/"recover" mechanism in Go to get exceptions.[2] Don't go there.

[1] http://lucumr.pocoo.org/2014/11/6/error-handling-in-rust/ [2] https://code.google.com/p/go-wiki/wiki/PanicAndRecover#Usage...


mercurial 14 days ago

> The secret of exception sanity is a standard exception hierarchy, like Python's. Catching an exception class in Python also gets all its subclasses. "EnvironmentError?" covers all I/O errors, network errors, and such, but doesn't cover internal program errors. New user-defined exceptions are added below the most appropriate part of the standard exception hierarchy. So Python usually doesn't have Java's exception hell.

You know, Java also has a standard exception hierarchy. However, whether in Java or Python, it usually makes sense for the exceptions of a library NOT to be part of the standard hierarchy. For instance, SQLAlchemy's exceptions all inherit from SQLAlchemyError?. It's the same thing in Java.

"

---

i might recommend multiply-inheriting exceptions then.. so eg something can be both an SQLAlchemyError?, and an EnvironmentError?.

---

this sort of thing is a real problem:

" You've got a deeply-nested set of objects that may or may not always be there. We've all seen something like this: var myURL = app.config.environment.buildURL('dev'); which leads to one of our favorite javascript errors... error: undefined is not a function

And the solution only makes the code base ugly:

var myURL; if (app && app.config && app.config.environment && app.config.environment.buildURL) { myURL = app.config.environment.buildURL('dev'); }

We all hate that, don't we? " -- https://github.com/letsgetrandy/brototype

or what if it is

app['soap:Envelope']['soap:Body'][0].getResponse[0]['rval'][0].customerId[0]

?

https://github.com/letsgetrandy/brototype is one soln but with a new language we can do better (by not having to repeat the thing twice)

---

make syntactic sugar to have a more structured version of:

        raise Exception('image_mask_onto_atlas_superimpose_pipeline: ABA returned failure; request = %s, parsed response = %s' % (url, data))

(Python lets you put other data in the fields of the Exception, this is too much typing; but if some of these were standardized (eg semantic name of the part of program raising the error; error msg; details) it could be made easier to type)

---

would be really nice if stack tracebacks printed not only everything that Python does, but also:


MichaelOChurch? complains that "Java’s type system lacks the power to verify that a String variable, at compile time, will actually hold a string, and not null, a value that must be handled specially by any method that takes a String (ditto, for any other object type) as input. The string type is not available; you must make do with what is, in fact, a (string option) ref— a mutable reference that may hold a String.".

Which suggests that:

---

to tame event-driven programming, propagation hop limit (preferably continuous, but i don't see how that would work)

(my friend G.B.'s idea)

---

(adjustable) limit on # of statements in a transaction, to guarantee termination

(my friend G.B.'s idea)

---

when transactions fail, retry afteer a raondom time interval to prevent the same sequence of events potentially recurring and causing starvation

---

pervasive patterns builtin to the std library and mb the language:

---

" Here's an example I post sometimes comparing error handling in Go to Haskell's either monad:

    func failureExample()(*http.Response) {
        // 1st get, do nothing if success else print exception and exit
        response, err := http.Get("http://httpbin.org/status/200")
        if err != nil {
            fmt.Printf("%s", err)
            os.Exit(1)
        } else {
            defer response.Body.Close()
        }
    
        // 2nd get, do nothing if success else print exception and exit
        response2, err := http.Get("http://httpbin.org/status/200")
        if err != nil {
            fmt.Printf("%s", err)
            os.Exit(1)
        } else {
            defer response2.Body.Close()
        }
    
    
        // 3rd get, do nothing if success else print exception and exit
        response3, err := http.Get("http://httpbin.org/status/200")
        if err != nil {
            fmt.Printf("%s", err)
            os.Exit(1)
        } else {
            defer response3.Body.Close()
        }
    
    
        // 4th get, return response if success else print exception and exit
        response4, err := http.Get("http://httpbin.org/status/404")
        if err != nil {
            fmt.Printf("%s", err)
            os.Exit(1)
        } else {
            defer response4.Body.Close()
        }
    
        return response4
    }
    
    func main() {
        fmt.Println("A failure.")
        failure := failureExample();
        fmt.Println(failure);
    }

The equivalent Haskell code:

    failureExample :: IO (Either SomeException (Response LBS.ByteString))
    failureExample = try $ do
      get "http://www.httpbin.org/status/200"
      get "http://www.httpbin.org/status/200"
      get "http://www.httpbin.org/status/200"
      get "http://www.httpbin.org/status/404"
    
    main = failureExample >>= \case
      Right r -> putStrLn $ "The successful pages status was (spoiler: it's 200!): " ++ show (r ^. responseStatus)
      Left e -> putStrLn ("error: " ++ show e)" -- https://news.ycombinator.com/item?id=9767846

scoped assertions: true at all times within the given scope (lexical, or variable lifetime)

---

http://marcelog.github.io/articles/erlang_link_vs_monitor_difference.html

---

special error/nil value for "this value isnt ready yet" (eg an unfulfilled promise) and for "havent even begun to compute this yet" (eg a lazy value that hasn't yet been requested, so computation of it hasnt even started) --by default Oot blocks when u look at a value, until that value is ready, so by default u dont see thse, but there is some way to explicitly request them (eg to check on the status of a promise). note that any part of a structure can be uncomputeed while the rest are computed (eg you can have x = [apple/RED lemon/NOT-READY-YET]) -- you need to use recursive any' to check if everything is done (mb 'rany' (recursive-any) or 'dany' (deep-any)? maybe these are just shortcuts for any(deep-flatten(x))? mb we have an is-ready as a shortcut for that w/r/t checking if there are no NOT-READY-YETs or NOT-BEGUN-COMPUTING-YET

probably NOT-BEGUN-COMPUTING-YET is a special case (child class) of NOT-READY-YET

more kinds of errors/classes of errors:

note that these classes are not necessarily orthogonal; eg an error might both be a connection error, and an 'there may or may not have been an error', at the same time.

---

need a sentinel for 'reset'; eg might have a function 'clip' to set a clipping rectange, which takes coordinates as input; pass it this sentinel to turn off clipping again

---

" Take the following C code:

    char* response(messy* message) {
      body* b = message->body;
      if(b==NULL) {
        return NULL;}
      char** lines = b->text;
      if(t==NULL) {
        return NULL;
      }
      if(b->line_count < 11) {
        return NULL;
      }
      return t[10];
    }

...

With Hackell, though, I can refactor out the if statements and get the following equivalent code:

    response :: Messy -> Maybe String
    response message = do
      b <- body message
      lines <- text body
      lines `atMay` 10

...

 With a minimal amount of effort, I could have each failure return a unique error Code.
    errorCode :: Maybe a -> b -> Either b a
    errorCode (Just value) _ = Right value
    errorCode Nothing code = Left code
    response :: Messy -> Either Int String
    response message = do
      b <- body message `errorCode` 1
      lines <- text body `errorCode` 2
      (lines `atMay` 10) `errorCode` 3

...

mightybyte 2 days ago

Just a small side note, you don't even need to write your errorCode function. That and a number of other useful functions are provided in the errors package (http://hackage.haskell.org/package/errors). Also, I like to use string error messages because they're easier to grep for. Here's what your response function would look like with those changes.

    response :: Messy -> Either String String
    response message = do
      b <- note "error in body" $ body message
      lines <- note "error in text" $ text b
      note "error in atMay" $ lines `atMay` 10

...

axman6 2 days ago

I've used a similar construct in dealing with some JSON data recently, using Either String a:

    (?) :: Maybe a -> String -> Either String a
    Nothing ? s = Left s
    Just a ? _ = Right a
    foo :: Value -> Either String Int
    foo val = do
        nested <- val ^? key "foo" . key "bar" ? "Could not find foo.bar"
        mid <- nested ^? nth 2 . _Integral ? "Second element of foo.bar was not an Integral value"
        return mid

which has really helped deal with the stringly typed nature of JSON. I should also mention I stole the idea (and operator name) from one of the Haskell parser combinator libraries which allowed you to name a whole parser expression, so errors would say "could not find ident" instead of "Expected on of a,b,c,d,e,d,f,g,h...., found X"

reply

GregBuchholz? 2 days ago

>I don't know a way of doing that in any of the C style languages

http://www.boost.org/doc/libs/1_49_0/libs/optional/doc/html/...

https://www.google.com/#q=maybe+in+c%2B%2B

...and also I couldn't resist...

    char* response(messy* message) {
        body* b = message->body;
        return b && (b->text) && b->linecount < 11 && b->text[10];
    }

...for a C-ish like language. And that's without creating a "do-notation" macro to erase the &&, as the Haskell do-notation erases the >>=.

reply

evincarofautumn 1 day ago

These aren’t quite the same. With “x && x->y”, you’re using a boolean to encode the assumption that “x” is valid, but with “x >>= getY”, the type system has a proof, which makes refactoring safer.

reply

"

-- https://news.ycombinator.com/item?id=10129307

---

need ' to be able to do:

error2none(x/100.)

(where 'x' might itself be None, generating an error)

the problem is that when x/100. is evaluated, the error is generated, before it is passed into error2none

so we have to take care of that. ' is more than just an ordinary function.

(in Python, you could do:

error2none(lambda x/100.)

def error2none(x): try: return x() except: return

but that's cumbersome

---

"For all the hate Java gets, when I have a checked exception, I know what it is going to be. I don't need to dig around in sourcecode trying to work out if I should care or not. I don't need to check which of the three Golang error-distinction idioms (package-variable errors, error subtypes, plain-old-errors-with-magical-strings) is in play."

---

 jacques_chester 2 days ago

Sorry, typo. "Checked exception", meaning that the compiler enforces error handling and that you can look a method signature and learn what kind of exceptions the method can throw without needing to read its internals.

---

c++ has a 'static_assert' presumably for platform dependent code:

" P.5: Prefer compile-time checking to run-time checking

Reason: Code clarity and performance. You don't need to write error handlers for errors caught at compile time.

Example:

void initializer(Int x) Int is an alias used for integers { static_assert(sizeof(Int)>=4); do: compile-time check

    int bits = 0;                   // don't: avoidable code
    for (Int i = 1; i; i<<=1)
        ++bits;
    if (bits<32)
        cerr << "Int too small\n";
    // ...} "

it also has an 'array_view' which i dont quite understand but presumably it carries array bounds information with it:

" Example; don't:

void read(int* p, int n); read max n integers into *p

Example:

void read(array_view<int> r); read into the range of integers r "

-- [1]

---

in scrapy (which uses Twisted), i see a good (addition?) to Python's stack trace; an '--- <exception caught here> ---' line:

2015-09-30 08:08:08 [twisted] CRITICAL: Unhandled error in Deferred:

Traceback (most recent call last): File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/cmdline.py", line 150, in _run_command cmd.run(args, opts) File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/commands/runspider.py", line 88, in run self.crawler_process.crawl(spidercls, opts.spargs) File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/crawler.py", line 153, in crawl d = crawler.crawl(*args, kwargs) File "/home/bshanks/.local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1274, in unwindGenerator return _inlineCallbacks(None, gen, Deferred()) --- <exception caught here> --- File "/home/bshanks/.local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1128, in _inlineCallbacks result = g.send(result) File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/crawler.py", line 71, in crawl self.engine = self._create_engine() File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/crawler.py", line 83, in _create_engine return ExecutionEngine?(self, lambda _: self.stop()) File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/core/engine.py", line 66, in __init__ self.downloader = downloader_cls(crawler) File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/core/downloader/__init__.py", line 65, in __init__ self.handlers = DownloadHandlers?(crawler) File "/home/bshanks/.local/lib/python2.7/site-packages/scrapy/core/downloader/handlers/__init__.py", line 17, in __init__ handlers.update(crawler.settings.get('DOWNLOAD_HANDLERS', {})) exceptions.ValueError?: dictionary update sequence element #0 has length 1; 2 is required

---

notes on (and quotes from): http://www.slideshare.net/deanwampler/error-handling-in-reactive-systems

approaches to error handling:

todo: on slide 43

---

esp. when making Exception hierarchies with multiple inheritance, you often want to create Exception classes in a 'faceted' or direct-product kind of way, eg:

class ExceptionDuringRead?(Exception): pass

class BrokerException?(Exception): pass

class BrokerExceptionDuringRead?(BrokerException?,