Marc:
I have the following top-level definitions:
(pp CRsqrt)
(lambda (x) (let ((args (check-args 'CRsqrt (list x)))) (with-exception-catcher (lambda (err) (##raise-range-exception 1 CRsqrt x)) (lambda () (make-CR (computable-sqrt (car args)))))))
with the intention that if computable-sqrt raises an exception, it's handled here. computable-sqrt is defined as (x is a procedure of one argument):
(pp computable-sqrt)
(lambda (x) (cond ((table-ref #:table0 x #f)) (else (let ((#:result1 (if (or (eq? x computable-zero) (eq? x computable-one)) x (let ((x_0 (x 0))) (if (> x_0 1) (let ((s (two^p<abs_m ;;<<<<<<<<<<<<<<<<<<<<<<<<<<< x_0))) (computable-memoize (lambda (k) (if (<= (* 2 k) s) (int-sqrt (* (expt 2 (* 2 k)) x_0)) (let ((n (- k (quotient s 2)))) (int-sqrt (* (expt 2 (- (* 2 k) n)) (x n)))))))) ;;>>>>>>>>>>>>>>>>>>>>>>>>>>> (computable-memoize (lambda (k) (let ((x_k (x k))) (cond ((negative? ;;<<<<<<<<<<<<<<<<<<<<<<<<<<< x_k) (error "computable-sqrt: argument is negative: " x)) ((< 1 x_k) (let ((s (two^p<abs_m x_k))) (let ((n (quotient (- (* 3 k) s) 2))) (int-sqrt (* (expt 2 (- (* 2 k) n)) (x n)))))) (else (let ((x_2k (x (* 2 k)))) (if (negative? x_2k) (error "computable-sqrt: argument is negative: " x) (int-sqrt x_2k))))))))))))) ;;>>>>>>>>>>>>>>>>>>>>>>>>>>> (table-set! #:table0 x #:result1) #:result1))))
computable-memoize is:
(pp computable-memoize)
(lambda (x) (let ((result-so-far #f) (i 0)) (lambda (k) ((letrec ((loop (lambda () (if (and result-so-far (<= k (car result-so-far))) (arithmetic-shift (cdr result-so-far) (- k (car result-so-far))) (let ((k* (+ k (integer-length k)))) (let ((result (x k*))) (set! result-so-far (cons k* ;;<<<<<<<<<<<<<<<<<<<<<<<<<<< result)) ;;>>>>>>>>>>>>>>>>>>>>>>>>>>> (loop))))))) loop)))))
And if computable-sqrt raises an exception, it hits at the
(let ((result (x k*)))
in computable-memoize as
(CR->string (CRsqrt #e-1e-100000) 100000)
*** ERROR IN loop, "basics.scm"@114.1 -- computable-sqrt: argument is negative: #<procedure #5> ┏━━━━━━━━━━━━━━━━━━━ basics.scm ━━━━━━━━━━━━━━━━━━━ ┃⋯ 113┃ 114┃(setup-primitives) 115┃ ┃⋯
(setup-primitives) is a macro that defines computable-memoize and a few other macros.
So, with what should I surround the call to (x k*) in computable-memoize to send the exception up to CRsqrt?
Brad
Afficher les réponses par date
My guess is that you have defined the `setup-primitives` macro using `define-macro`. The issue is that `define-macro` operates directly on raw S-expression representations and produces a S-expression which will contain very inaccurate location information (it will tag the produced S-expression with the location of the call to `setup-primitives`).
To avoid this issue you need to write the macro using `define-syntax` which can preserve accurate location information.
Here is a simple macro defined using `define-macro`. Notice that when the division by zero happens the message contains the location of the macro call, even though the error happened in the expression (/ 1 0):
(define-macro mymac-dm (lambda (arg) `(let ((x (+ 1 ,arg))) (* x x))))
(mymac-dm (/ 1 0))
$ gsi macros.scm |cat *** ERROR IN "macros.scm"@6.1 -- Divide by zero (/ 1 0) ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ macros.scm ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┃⋯ 5┃ 6┃(mymac-dm (/ 1 0)) ┃▔ 7┃ ┃⋯
Here is how the same macro can be defined using `define-syntax` which preserves the location information of the macro argument:
(define-syntax mymac-ds (lambda (stx) (let ((stx-code (##source-code stx))) (if (and (pair? stx-code) (eq? (##source-code (car stx-code)) 'mymac-ds) (pair? (cdr stx-code)) (null? (cddr stx-code))) (let ((arg (cadr stx-code))) `(let ((x (+ 1 ,arg))) (* x x))) (error "mymac-ds syntax error")))))
(mymac-ds (/ 1 0))
$ gsi macros.scm |cat *** ERROR IN "macros.scm"@13.11-13.18 -- Divide by zero (/ 1 0) ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ macros.scm ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┃⋯ 12┃ 13┃(mymac-ds (/ 1 0)) ┃ ▔▔▔▔▔▔▔ 14┃
It is really tedious to parse the macro arguments using `##source-code`… a much better way is to use `syntax-rules` which will also work because the pattern matcher and expander used by `syntax-rules` preserves the location information (it is implemented using `##source-code`):
(define-syntax mymac-sr (syntax-rules () ((_ arg) (let ((x (+ 1 arg))) (* x x)))))
(mymac-sr (/ 1 0))
$ gsi macros.scm |cat *** ERROR IN "macros.scm"@7.11-7.18 -- Divide by zero (/ 1 0) ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ macros.scm ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┃⋯ 6┃ 7┃(mymac-sr (/ 1 0)) ┃ ▔▔▔▔▔▔▔
Marc
On Sep 9, 2025, at 9:58 PM, Bradley Lucier via Gambit-list gambit-list@iro.umontreal.ca wrote:
Marc:
I have the following top-level definitions:
(pp CRsqrt)
(lambda (x) (let ((args (check-args 'CRsqrt (list x)))) (with-exception-catcher (lambda (err) (##raise-range-exception 1 CRsqrt x)) (lambda () (make-CR (computable-sqrt (car args)))))))
with the intention that if computable-sqrt raises an exception, it's handled here. computable-sqrt is defined as (x is a procedure of one argument):
(pp computable-sqrt)
(lambda (x) (cond ((table-ref #:table0 x #f)) (else (let ((#:result1 (if (or (eq? x computable-zero) (eq? x computable-one)) x (let ((x_0 (x 0))) (if (> x_0 1) (let ((s (two^p<abs_m ;;<<<<<<<<<<<<<<<<<<<<<<<<<<< x_0))) (computable-memoize (lambda (k) (if (<= (* 2 k) s) (int-sqrt (* (expt 2 (* 2 k)) x_0)) (let ((n (- k (quotient s 2)))) (int-sqrt (* (expt 2 (- (* 2 k) n)) (x n)))))))) ;;>>>>>>>>>>>>>>>>>>>>>>>>>>> (computable-memoize (lambda (k) (let ((x_k (x k))) (cond ((negative? ;;<<<<<<<<<<<<<<<<<<<<<<<<<<< x_k) (error "computable-sqrt: argument is negative: " x)) ((< 1 x_k) (let ((s (two^p<abs_m x_k))) (let ((n (quotient (- (* 3 k) s) 2))) (int-sqrt (* (expt 2 (- (* 2 k) n)) (x n)))))) (else (let ((x_2k (x (* 2 k)))) (if (negative? x_2k) (error "computable-sqrt: argument is negative: " x) (int-sqrt x_2k))))))))))))) ;;>>>>>>>>>>>>>>>>>>>>>>>>>>> (table-set! #:table0 x #:result1) #:result1))))
computable-memoize is:
(pp computable-memoize)
(lambda (x) (let ((result-so-far #f) (i 0)) (lambda (k) ((letrec ((loop (lambda () (if (and result-so-far (<= k (car result-so-far))) (arithmetic-shift (cdr result-so-far) (- k (car result-so-far))) (let ((k* (+ k (integer-length k)))) (let ((result (x k*))) (set! result-so-far (cons k* ;;<<<<<<<<<<<<<<<<<<<<<<<<<<< result)) ;;>>>>>>>>>>>>>>>>>>>>>>>>>>> (loop))))))) loop)))))
And if computable-sqrt raises an exception, it hits at the
(let ((result (x k*)))
in computable-memoize as
(CR->string (CRsqrt #e-1e-100000) 100000)
*** ERROR IN loop, "basics.scm"@114.1 -- computable-sqrt: argument is negative: #<procedure #5> ┏━━━━━━━━━━━━━━━━━━━ basics.scm ━━━━━━━━━━━━━━━━━━━ ┃⋯ 113┃ 114┃(setup-primitives) 115┃ ┃⋯
(setup-primitives) is a macro that defines computable-memoize and a few other macros.
So, with what should I surround the call to (x k*) in computable-memoize to send the exception up to CRsqrt?
Brad
Gambit-list mailing list -- gambit-list@iro.umontreal.ca To unsubscribe send an email to gambit-list-leave@iro.umontreal.ca
On 9/9/25 22:46, Marc Feeley wrote:
My guess is that you have defined the `setup-primitives` macro using `define-macro`.
Thanks. To simplify things I got rid of the macro, that's not the issue.
The issue is that the result of computable-sqrt, where the error is raised, is not called in tail position, and where it is called the handler I defined is not invoked:
(CR->string (CRsqrt #e-1e-100000) 100000)
*** ERROR IN loop, "basics.scm"@62.37-62.43 -- computable-sqrt: argument is negative: #<procedure #5> ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ basics.scm ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┃⋯ 61┃ (let* ((k* (+ k (integer-length k))) 62┃ (result (x k*))) ;; << points here 63┃ (set! result-so-far (cons k* result)) ┃⋯ 1>
So what do I wrap this call to (x k*) in to get it to pass up the exception to the handler in CRsqrt, which is
(pp CRsqrt)
(lambda (x) (let ((args (check-args 'CRsqrt (list x)))) (with-exception-catcher (lambda (err) (##raise-range-exception 1 CRsqrt x)) (lambda () (make-CR (computable-sqrt (car args)))))))
On 9/9/25 22:46, Marc Feeley wrote:
a much better way is to use `syntax-rules`
OK, here are the macros I use over and over. Can you offer some guidance about how to rewrite them?
Brad
(define-macro (computable-lambda arg . body) (let ((result `(computable-memoize (lambda ,arg ,@body)))) result))
(define-macro (table-memoize arg . body) (let ((table-name (gensym 'table)) (result-name (gensym 'result))) (let ((result `(let ((,table-name (make-table weak-keys: #t weak-values: #t test: eqv?))) (lambda ,arg (cond ((table-ref ,table-name ,(car arg) #f)) (else (let ((,result-name (let () ,@body))) (table-set! ,table-name ,(car arg) ,result-name) ,result-name))))))) result)))
(define-macro (macro-make-incremental-power-series x ;; name of argument partial-term ;; (lambda (m n) ...) common-factor-ratio ;; (lambda (m n) ...) k*->terms ;; (lambda (k*) ...)
multiply-series-by-x? ;; if #f, multiply by computable-one debug-name ;; name to print in debug messages ) (let ((result `(table-memoize (,x) (letrec* ((partial-term ,partial-term) (common-factor-ratio ,common-factor-ratio) (k*->terms ,k*->terms) (multiply-series-by-x? ,multiply-series-by-x?) (number-of-terms #f) (sum-of-terms #f) (k* #f) (v* #f))
(define (reset-k*) (if *strict-testing* (set! k* #f)))
(define (result k) (cond ((not k*) (if (*debug*) (pp (list ,debug-name new-power-series: arg: ,x k: k))) (set! k* (k->k* k)) (set! number-of-terms (k*->terms k*)) (set! sum-of-terms (computable-* (if multiply-series-by-x? ,x computable-one)
(computable-binary-splitting-partial-sum 0 number-of-terms partial-term common-factor-ratio))) (set! v* (sum-of-terms k*)) (result k)) ((<= k k*) (if (*debug*) (pp (list ,debug-name found-existing-value: arg: ,x k*: k* k: k number-of-terms: number-of-terms))) (let ((result (arithmetic-shift v* (- k k*)))) (reset-k*) result)) (else (if (*debug*) (pp (list ,debug-name extending-power-series: arg: ,x k*: k* k: k))) (let* ((new-k* (k->k* k)) (new-number-of-terms (max number-of-terms (k*->terms new-k*))) (new-sum-of-terms (computable-+ sum-of-terms (computable-* (if multiply-series-by-x? ,x computable-one) (computable-* (computable-binary-splitting-partial-sum number-of-terms new-number-of-terms partial-term common-factor-ratio) (common-factor-ratio 0 number-of-terms))))) (new-v* (new-sum-of-terms new-k*))) (set! k* new-k*) (set! v* new-v*) (set! number-of-terms new-number-of-terms) (set! sum-of-terms new-sum-of-terms) (result k)))))
result))))
result))
I got rid of every define-macro (lambda forever!) and I still have the problem, but it's very strange.
If I "include" or "##include" a file that has the definition of ->computable, I have the problem:
heine:~/text/courses/computation/computational-reals/app/crap/minimal> gsi CReals.scm *** ERROR IN loop, "basics.scm"@62.30-62.36 -- computable-sqrt: argument is negative: #<procedure #2> ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ basics.scm ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┃⋯ 61┃ (let* ((k* (+ k (integer-length k))) 62┃ (result (x k*))) 63┃ (set! result-so-far (cons k* result)) ┃⋯
But if I *don't* include the file but include the definition of ->computable by itself:
#; (include "arithmetic.scm")
(define (->computable r) (if (not (rational? r)) (error "->computable: argument is not rational: " r) (let ((r (exact r))) (cond ((integer? r) (case r ((0) computable-zero) ((1) computable-one) ((-1) computable-negative-one) (else (lambda (n) (arithmetic-shift r n))))) ;;<< the result ((power-of-two? (denominator r)) (let ((num (numerator r)) (log_2-den (first-set-bit (denominator r)))) (lambda (n) (arithmetic-shift num (- n log_2-den))))) (else (computable-memoize (lambda (n) (round (* r (expt 2 n))))))))))
then the problem goes away!!!!
heine:~/text/courses/computation/computational-reals/app/crap/minimal> gsi CReals.scm *** ERROR IN "CReals.scm"@232.13-232.24 -- (Argument 1) Out of range (CRsqrt -5) ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ CReals.scm ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┃⋯ 231┃ 232┃(CR->string (CRsqrt -5))
No define-macro's:
grep define-macro *
Suggestions?
Brad
On 9/10/25 14:51, Bradley Lucier wrote:
But if I *don't* include the file but include the definition of -
computable by itself:
#; (include "arithmetic.scm")
(define (->computable r) (if (not (rational? r)) (error "->computable: argument is not rational: " r) (let ((r (exact r))) (cond ((integer? r) (case r ((0) computable-zero) ((1) computable-one) ((-1) computable-negative-one) (else (lambda (n) (arithmetic-shift r n))))) ;;<< the result ((power-of-two? (denominator r)) (let ((num (numerator r)) (log_2-den (first-set-bit (denominator r)))) (lambda (n) (arithmetic-shift num (- n log_2-den))))) (else (computable-memoize (lambda (n) (round (* r (expt 2 n))))))))))
then the problem goes away!!!!
And if I comment out everything in arithmetic.scm except for the definition of ->computable and include arithmetic.scm, the problem also goes away.
Giving up for now.
But no macros!
Brad
On 9/10/25 15:17, Bradley Lucier wrote:
Giving up for now.
As mentioned here:
https://github.com/gambit/gambit/issues/977
I am absolutely wrong about under what circumstances the error is being raised, and it is being raised outside the with-error-catcher form.
I'll have to rethink my error handling process.
Sorry for *all* the spam.
Brad