(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.

Reply via email to