(By "integrating" with the new strategy, I meant having two keys: one for the new strategy and one for the old strategy. I can see that the first entry of the old strategy is useful, and it's missing in the new strategy).
On Sun, Jul 26, 2020 at 8:21 PM Sorawee Porncharoenwase < [email protected]> wrote: > Hi everyone, > > I have a question about the implementation of errortrace. > > Consider the classic factorial program, except that the base case is buggy: > > (define (fact m) > (let loop ([n m]) > (cond > [(zero? n) (/ 1 0)] > [else (* (loop (sub1 n)) n)]))) > > (fact 5) > > Running this program with racket -l errortrace -t fact.rkt gives the > following output: > > /: division by zero > errortrace...: > /Users/sorawee/playground/fact.rkt:9:17: (/ 1 0) > /Users/sorawee/playground/fact.rkt:10:12: (* (loop (sub1 n)) n) > /Users/sorawee/playground/fact.rkt:10:12: (* (loop (sub1 n)) n) > /Users/sorawee/playground/fact.rkt:10:12: (* (loop (sub1 n)) n) > /Users/sorawee/playground/fact.rkt:10:12: (* (loop (sub1 n)) n) > /Users/sorawee/playground/fact.rkt:10:12: (* (loop (sub1 n)) n) > > I find this result subpar: it doesn’t indicate which call at the top-level > leads to the error. You can imagine another implementation of fact that > errors iff m = 5. Being able to see that (fact 5) at the top-level causes > the error, as opposed to (fact 3), would be very helpful. > > Not only that, (* (loop (sub1 n)) n) also looks weird. There’s nothing > wrong with multiplication, so I don’t find this information useful. > > The tail-recursive factorial is similarly not helpful: > > (define (fact m) > (let loop ([n m] [acc 1]) > (cond > [(zero? n) (/ 1 0)] > [else (loop (sub1 n) (* n acc))]))) > > (fact 5) > > produces: > > /: division by zero > errortrace...: > /Users/sorawee/playground/fact.rkt:9:17: (/ 1 0) > > ------------------------------ > > I have been toying with another way to instrument the code. It roughly > expands to: > > (define-syntax-rule (wrap f) > (call-with-immediate-continuation-mark > 'errortrace-k > (λ (k) > (let ([ff (thunk f)]) > (if k > (ff) > (with-continuation-mark 'errortrace-k 'f > (ff))))))) > > (define (handler ex) > (continuation-mark-set->list (exn-continuation-marks ex) 'errortrace-k)) > > (define (fact m) > (wrap (let loop ([n m]) > (wrap (cond > [(wrap (zero? n)) (wrap (/ 1 0))] > [else (wrap (* (wrap n) (wrap (loop (wrap (sub1 > n))))))]))))) > > (with-handlers ([exn:fail? handler]) > (wrap (fact 5))) > > which produces: > > '((loop (wrap (sub1 n))) > (loop (wrap (sub1 n))) > (loop (wrap (sub1 n))) > (loop (wrap (sub1 n))) > (loop (wrap (sub1 n))) > (fact 5)) > > This result is more aligned with the traditional stacktrace, and gives > useful information that I can use to trace to the error location. > > It is also safe-for-space: > > (define (fact m) > (wrap (let loop ([n m] [acc 1]) > (wrap (cond > [(wrap (zero? n)) (wrap (/ 1 0))] > [else (wrap (loop (wrap (sub1 n)) (wrap (* n acc))))]))))) > > (with-handlers ([exn:fail? handler]) > (wrap (fact 5))) > > produces: > > '((fact 5)) > > Now, the question: why is the current errortrace implemented in that way? > Am I missing any downside of this new strategy? Would switching and/or > integrating with the new strategy be better? > > Thanks, > Sorawee (Oak) > -- 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 [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/CADcuegto9%2BDtFTwAVmiReOcCwpARzBSbFhF0knyexb7UhoHQiA%40mail.gmail.com.

