
@rntz You can use browse-syntax
from the macro-debugger/syntax-browser
library. Or use the macro stepper.

I’m struggling to to report the correct location of an error from a macro-generating macro. File “safe-ops.rkt”: #lang racket/base
(require racket/flonum
racket/math
(for-syntax racket/base
racket/math
racket/syntax-srcloc)
syntax/parse/define)
(define-syntax (make-safe-ops stx)
(syntax-parse stx
[(_ op ...)
(with-syntax ([(op2 ...) (generate-temporaries #'(op ...))])
#'(begin
(provide (rename-out [op2 op] ...))
(define-syntax (op2 stx2)
(syntax-parse stx2
[(_ a (... ...))
(with-syntax ([loc (srcloc->string (syntax-srcloc #'stx2))])
#'(let ([res (op a (... ...))])
(if (or (nan? a) (... ...) (nan? res))
(error "Got nan!" loc)
res)))]))
...))]))
(make-safe-ops fl+ fl-)
Then in a second file “try-safe-ops.rkt”: #lang racket
(require "safe-ops.rkt")
(fl+ +nan.0 1.)
But this reports: safe-ops.rkt:22:26: Got nan! "safe-ops.rkt:19:68"
I’m particularly intrigued by the location reported on the RHS of the line above. How do I make it report “try-safe-ops.rkt:4:0” instead?

(Context: I have some +nan.0
s in my big programs that I catch too late. I’m trying to substitute the fl
operations with ones that raise an exception as soon as a +nan.0
is used.)
The fl+
exported by safe-ops.rkt
is a macro that wraps around the original fl+
, and raises an exception if its arguments or result are +nan.0

A bit of unpacking: The line with-syntax ([loc ...
is supposed to catch the location of the call site of (fl+ +nan.0 1.)
but apparently catches the location of the definition of the new fl+
, which is surprising to me.

One of the problems is that you shouldn’t quote stx2
with #'

But also, let me try to rewrite that in a way that I like it

(define-syntax-parse-rule (make-safe-ops op ...)
#:with (op2 ...) (generate-temporaries (attribute op))
#:with ooo (quote-syntax ...)
(begin
(provide (rename-out [op2 op] ...))
(define-syntax-parse-rule (op2 a ooo)
#:with error-expr (syntax/loc this-syntax
(error "Got nan!"))
#:with op-expr (syntax/loc this-syntax (op a ooo))
(let ([res op-expr])
(if (or (nan? a) ooo (nan? res))
error-expr
res)))
...))

racket -l errortrace -t try-safe-ops.rkt
would report the correct location

In your current code, errortrace would report wrong location regardless of the fix I suggested above

The fix for that is to use syntax/loc

Why making this a macro though? Simple functions would suffice, right? You can e.g. make a higher order function that wraps around unsafe variants to make the m safe.

@laurent.orseau In case you do use syntax/loc
: I notice your macro expands to a begin
wrapping a couple pieces. You might need to use syntax/loc
on a specific piece — not the entire begin
.
Although I don’t know if it’s the best explanation, I blogged about this before: https://www.greghendershott.com/2014/01/using-syntax-loc.html

@sorawee Aha, it’s indeed stx2
and not #'stx2
, that all makes sense now, thanks a bunch! I was about to use syntax/loc
but I first wanted to make sure I had the right location.
@sorawee {function} :face_palm: :face_palm: :face_palm: Duh. Duh, duh, duh.
@greg thanks!

@sorawee Unduh. Actually, how do you enforce that the function call site is reported by errortrace? With just this: (define-syntax-parse-rule (make-safe-ops op ...)
#:with (op2 ...) (generate-temporaries #'(op ...))
#:with … (quote-syntax ...)
(begin
(provide (rename-out [op2 op] ...))
(define (op2 . args)
(let ([res (apply op args)])
(if (or (ormap nan? args) (nan? res))
(error "Got nan!" args res)
res)))
...))
the location is where the code (error ...)
is written in the file, while (I assume) we don’t have access to the syntax location of the call site by contrast to the macro.
Edit: and that’s what Greg’s post is actually about :slightly_smiling_face: will try later.

Yes, and also see my above code, which does what Greg suggested

Ah wait no, that’s still not good enough, because greg’s code gives the function definition site, not the call site. Your code above works because it’s a macro, for which we can obtain the call site syntax.
So I was right initially, the operators need to be defined as macros so the exception is caught at the call site, right?

Well, if there is no macro at all, you will get wrong srcloc at the top of the stack, but the stack should contain a frame with the right srcloc

But if you use macro, then my approach is what you should do

@sorawee Maybe because I typically use quaisyntax to do this, my eyes bounced over your #:with op-expr (syntax/loc this-syntax (op a ooo))
. I wish I’d phrased my suggestion in terms of the example you were already showing. Sorry!

Oh, no worries! :slightly_smiling_face:

“Here’s Why You Should Try This One Neat Trick that Someone is Already Showing You” :smile:

Well, I learned syntax/loc
mostly from your blog, so thank you!

Well I learned the “Just because you use syntax/loc
on the outer syntax doesn’t mean it magically applies to the piece you want” thing from @samth, so we should really both thank him.

Does anyone know of a document/blog entry/etc. that compares programming in Racket to programming in a language like Go? I’m still trying to get the hang of how to think of the structure of applications in Racket and it’s something to get the mind wrapped around…I mean, in (for example) Go, you start with some imports, a package name, a main(), and in main(), I can call another function that passes back the result (and maybe an error), then call another function, just keep passing the data through these functions or hand them off to another routine via a channel that maybe passes the result to a structure or through a network connection that was passed as an argument to it. You create various steps to operate on data that is passed around. I find I need to check data at some point, I can write another function and just insert it into the flow. Racket doesn’t seem to quite do that, it’s…operating on lists and handles data through recursion?
I suppose I’m looking for a way to model the flow of data in my head. I have read articles that describe Racket (and Lisps and Schemes) as being difficult until it just sort of…“clicks”, then it’s a joy to work with. It sounds like there’s a hurdle to comprehend how to “get” it and experience in other languages can hinder that inflection point? I wasn’t sure if I was just a little too dim to understand how to use the language but then I saw something about debugging in Racket where it’s not quite as useful to use a traditional debugger with the traditional methods, instead using the REPL to test and check functions (please feel free to correct me if I’m wrong there…)
I wondered then if there’s a good description of something to help get the in-head modeling of programs written in Racket described. Or if anyone can share some insight on this inflection point in learning the language, or if I’m off base on this impression?

Looks like the <https://www.cs.utah.edu/plt/snapshots/current/installers/racket-current-x86_64-win32-bc.exe|BC snapshot> started crashing on Windows yesterday: https://github.com/Bogdanp/setup-racket/runs/6586625978?check_suite_focus=true#step:10:1 .