| Okay, this is really annoying. First, what I'm trying to do: write a function that calls code that could potentially raise an exception. Okay, no problem, just use errsafe... but there's a catch. I also want to access whatever it was that was raised. So, if the function calls (err "foo") I want to be able to access the "foo" part[1]. This is very very very easy to do in Python (and JavaScript): try:
raise Exception("foo")
except Exception as e:
print str(e) # prints the string "foo"
After some poking and prodding, I made a tiny bit of progress. First, the on-err function lets you get access to the exception, so I tried that: (on-err (fn (x) (prn x))
(fn () (err "foo")))
; prints #(struct:exn:fail foo #<continuation-mark-set>)
Okay, so exceptions are structs... I then tried looking up the Racket documentation for how to access the struct's attributes, but no dice. I tried this: ((struct->vector c) 1) ; c is the exception
But I get the following error: procedure application: expected procedure, given: #(struct:exn:fail:contract "procedure application: expected procedure, given: #(struct:exn:fail \"foo\" #<continuation-mark-set>); arguments were: 1" #<continuation-mark-set>); arguments were: 1
Which is about as helpful as ... well, it isn't helpful. Okay, so, did some more reading... learned about vector-ref (gosh Racket is so much clunkier than Arc is...): (vector-ref (struct->vector c) 1) ; another error, yay
Okay, I just found exn-message... this really is a lot clunkier than it could be. sigh I feel kinda silly now, but I'm gonna leave this post here, in case somebody else has the same problem. I had already looked at the Exceptions documentation, but somehow missed that function. To my credit, the documentation for exn-message is pretty much... nonexistent. I accidentally learned about it while reading ac.scm.I think we should figure out a better way of dealing with exceptions... I'm definitely going to make exceptions a lot easier to deal with in Arubic, which should be a piece of cake (hopefully) since Python already has such nice support for them. An idea: uncaught exceptions are simply output to stderr, so you can use w/stderr or similar to catch them. Also, if you want to send special information in the exception, you should be using a complex data type anyways: (err (obj ...))
Then we can get rid of the `details` function. Yay less primitives! And let's change errsafe so it accepts a second parameter: a function that is called if the code raises an exception: (errsafe ... (fn (x) x))
The default would be to return the exception, since I think that's what you want, most of the time: (errsafe (err "foo")) -> "foo"
(errsafe (err '(1 2 3))) -> (1 2 3)
(errsafe (+ 3 4)) -> 7
---* [1]: In case you're wondering why I want to do this, here's the more detailed explanation. I'm writing a function that filters a list (the input) according to another list (the patterns). The default behavior is to throw an error when a pattern doesn't match anything in the input, or when a pattern matches twice, etc. You can override this behavior by passing in a function... your function will be called once for every missing pattern. This all works fine, no problem, but what I want it to do is, rather than display one error and then fail... I want it to display all the errors. I figured I could use something like on-err to collect the errors into a list, and then display them all at once. But if I can't access what was thrown, I obviously can't do that. Basically, I want this to work: (= errors nil)
(each ...
(push (errsafe (foo)) errors))
errors -> ("foo" "bar" "qux")
Assume that the function foo throws the errors "foo", then "bar", then "qux". To get this working, I only had to change one line in arc.arc: (mac errsafe (expr)
`(on-err (fn (c) (details c))
(fn () ,expr)))
Note the call to `details`, rather than returning nil. Hurray for taking over an hour just to figure out the single line I needed to change... this really isn't helping my gut feeling that Scheme is too bloated and hard to work with. :P |