On 03/31/2017 04:00 PM, David Storrs wrote:
Imagine I have the following trivial module (ignore that things are
defined out of sequence for clarity):
#lang racket
(define (foo arg)
(_baz arg) ; do some checking, raise an exception if there's a problem
...do stuff...
)
(define (bar arg)
(_baz arg) ; do some checking, raise an exception if there's a problem
...do stuff....)
(define (_baz arg) ; internal helper function
(when (equal? arg "bad value")
(raise-arguments-error '_baz "got a bad value" "val" val))) ;;
<=== report is from _baz, not foo or bar
(provide foo bar)
In Perl I can use caller() [1] to access the stack so that I can have
_baz report errors as though the error were coming from 'foo' or 'bar'
as appropriate -- which is what the user expects to see, since their
code called 'foo' or 'bar', not '_baz'. (Even better, I could report as
though the error were coming from the *caller* of foo / bar, which is
the user's actual code and the real source of the problem.) How can I do
the same in Racket? I see the errortrace library, but that says "don't
install this into a module and expect it to work."
I could pass the name down, of course, but that's clumsy. I could also
create a parameter and set it every time I call the helper function that
needs it, but that's even more clumsy / action-at-a-distance-y and easy
to forget. What's the right way?
You've just described the two ways that I know of. I call the first one
"WHO-passing style", and I use it a lot. I agree, it's a bit cumbersome.
A few years ago I experimented with taking the idea to the extreme. I
wound up with something like this:
(define (foo x y #:who [who 'foo])
.... (when .... (error who "something bad happened"))
.... (bar z #:who who) ....)
Within a library, every function that might raise an error has an
optional #:who argument. Every direct call to another such function in
the same library passes along the current who argument. Finally, the
main module provides the entry point functions with contracts that omit
the #:who argument.
It might be a fun macro exercise to make a special definition form that
automates "WHO-passing style", if anyone is interested :)
--
The parameter approach is difficult to get right. Here are two test
cases. For both, assume there are two "entry point" functions, foo and bar.
1. If foo calls bar, and bar raises an error, the error should appear to
come from foo. (Because that's the function the client called directly.)
2. If foo takes a procedure argument, and that procedure (originating
from the client) calls bar, and bar raises an error, the error should
appear to come from bar. (Even though the client called foo first and
foo has not yet returned, control passed back to the client, and the
error was triggered by a separate call to bar.)
The contract system already deals with the notion of boundaries between
parties. Maybe there's a way to extend it to provide a more satisfactory
solution to this problem.
Ryan
--
You received this message because you are subscribed to the Google Groups "Racket
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.