By changing (fact 5) to (* 2 (fact 5)), the stack information becomes

/: division by zero
  errortrace...:
   /Volumes/ramdisk/fact.rkt:6:17: (/ 1 0)
   /Volumes/ramdisk/fact.rkt:7:12: (* (loop (sub1 n)) n)
   /Volumes/ramdisk/fact.rkt:7:12: (* (loop (sub1 n)) n)
   /Volumes/ramdisk/fact.rkt:7:12: (* (loop (sub1 n)) n)
   /Volumes/ramdisk/fact.rkt:7:12: (* (loop (sub1 n)) n)
   /Volumes/ramdisk/fact.rkt:7:12: (* (loop (sub1 n)) n)
   /Volumes/ramdisk/fact.rkt:9:0: (* 2 (fact 5))

Here, the difference is that (fact 5) is no longer at tail position. I
believe errortrace is aiming at preserving proper tail implementation
behavior.

On Mon, Jul 27, 2020 at 11:39 AM Sorawee Porncharoenwase
<[email protected]> wrote:
>
> (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.

-- 
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/CAMTzy%2BbkerQnMuNA3%3Do6QHSDy7PZAPPRo6s19G21ECBMKwqHFw%40mail.gmail.com.

Reply via email to