ryanc
2022-5-24 10:32:20

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


laurent.orseau
2022-5-24 17:10:42

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?


laurent.orseau
2022-5-24 17:11:57

(Context: I have some +nan.0s 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


laurent.orseau
2022-5-24 17:16:44

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.


sorawee
2022-5-24 18:03:25

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


sorawee
2022-5-24 18:09:40

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


sorawee
2022-5-24 18:09:43

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


sorawee
2022-5-24 18:11:24

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


sorawee
2022-5-24 18:14:05

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


sorawee
2022-5-24 18:14:22

The fix for that is to use syntax/loc


sorawee
2022-5-24 18:16:43

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.


greg
2022-5-24 19:21:49

@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


laurent.orseau
2022-5-24 19:28:26

@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!


laurent.orseau
2022-5-24 19:44:52

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


sorawee
2022-5-24 20:48:09

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


laurent.orseau
2022-5-24 21:24:53

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?


sorawee
2022-5-24 21:26:39

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


sorawee
2022-5-24 21:27:25

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


greg
2022-5-25 00:38:46

@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!


sorawee
2022-5-25 00:39:32

Oh, no worries! :slightly_smiling_face:


greg
2022-5-25 00:39:46

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


sorawee
2022-5-25 00:40:48

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


greg
2022-5-25 00:41:59

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.


bsilverstrim
2022-5-25 01:57:34

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?


popa.bogdanp
2022-5-25 06:09:44

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 .