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.

Reply via email to