I ran into a series of confusing Racket things today.  Although I've fixed 
my code, I'm looking for insight.  Clearly there are things I don't 
understand about scribble/srcdoc, and maybe macros in general.

I have a macro called define/contract/doc.  It lets me do things like this:

(define/contract/doc (test #:number [number 75.2])

  (->i ()

       (#:number     [number number?])

       [returns any/c])


  @{Returns what you pass in for #:number.  Defaults to 75.2}


  number)


It has worked fine for almost a year.  But today, I discovered that when I 
define a function with a default parameter value that happens to include a 
decimal number (i.e. 75.2 above), I get a cryptic error when requiring in 
the srcdoc submodule.

I'll post the macro definition in a moment, but first let me mention that 
on the Windows machine where my co-worker first discovered this bug, the 
error was presented without a stacktrace.  This was it:

-: contract violation

  expected: number?

  given: #f

  argument position: 1st

  other arguments...:

   2

  context...:

*Puzzle #1.  *I don't usually develop on Windows, so can someone tell me if 
there's something special I'm supposed to do to get more context to print 
out?  Note: Even in the Racket REPL, I couldn't get a backtrace.  Using 
errortrace changed nothing.  I'm on Racket 7.4, btw.

It was only after poking, prodding, and simplifying for an hour that we 
extracted a minimal reproduction of the bug and figured out that the 
decimal number was causing the problem.  The lack of stacktrace led to a 
lot of guesswork.

I would have been at a loss, but I happened to switch to my Mac, where 
(luckily!) the error printed with a better stacktrace:

-: contract violation

  expected: number?

  given: #f

  argument position: 1st

  other arguments...:

   2

  context...:

   /Applications/Racket 
v7.4/share/pkgs/scribble-lib/scribble/racket.rkt:227:2: typeset-atom

   /Applications/Racket 
v7.4/share/pkgs/scribble-lib/scribble/racket.rkt:471:8

   /Applications/Racket 
v7.4/share/pkgs/scribble-lib/scribble/racket.rkt:322:2: gen-typeset

   /Applications/Racket 
v7.4/share/pkgs/scribble-lib/scribble/private/manual-proc.rkt:549:6

   /Applications/Racket v7.4/collects/racket/private/map.rkt:259:4: loop

   /Applications/Racket v7.4/collects/racket/list.rkt:586:2: append-map

   /Applications/Racket 
v7.4/share/pkgs/scribble-lib/scribble/private/manual-proc.rkt:316:2: do-one

   /Applications/Racket v7.4/collects/racket/private/map.rkt:259:4: loop

   /Applications/Racket v7.4/collects/racket/list.rkt:586:2: append-map

   /Applications/Racket 
v7.4/share/pkgs/scribble-lib/scribble/private/manual-proc.rkt:212:0: 
*defproc13

   (submod "/Users/thoughtstem/Dev/test/test.rkt" srcdoc): [running body]

   temp37_0

   for-loop

   run-module-instance!125

   perform-require!78

   for-loop

   ...

This informed me that the problem was related to typeset-atom, which in 
turn helped me to make slightly more educated guesses about how to fix the 
problem.

*Puzzle #2*.  If you save the snippet below into a file called test.rkt, you 
can reproduce the error and/or my fixes with something like racket -e 
"(require (submod \"./test.rkt\" srcdoc))".  Why does the datum->syntax fix 
the problem?  After skimming the srcdoc code, I suspect some line-number 
information needs to be present on the syntax being typeset....  Why this 
only matters for numbers with decimals is still a mystery.

#lang at-exp racket


(require (for-syntax syntax/parse))


(define-syntax (define/contract/doc stx)

  (define defaults 

    '(75) ;Works

    #;

    '(75.2) ;Breaks


    #;

    (datum->syntax stx '(75.2) stx) ;Works, and is essentially the way we 
fixed it.


    #;

    (datum->syntax stx '(75.2)) ;Breaks, and is roughly what we had in 
production -- without the hardcoded 75.2, obviously.

    )


  (define ret

    (syntax-parse stx

      ([_ (f-name args ... ) contract doc body ...]

       #`(begin

           (require scribble/srcdoc)

           (provide (proc-doc

                      f-name

                      contract

                      #,defaults 

                      doc)) 

           (define (f-name args ...)

             body ...)))))


  ret)


(define/contract/doc (test #:number [number 75.2])

  (->i ()

       (#:number [number number?])

       [returns any/c])


  @{Returns what you pass in for #:number.  Defaults to 75.2}


  number)


*Puzzle #3*.  What's the appropriate code to "blame" here?  Is this some 
kind of noob mistake on my part (i.e. advanced Racket macrologists would 
have known to use datum->syntax appropriately and never would have run into 
the problem in the first place)?  Is this a bug in srcdoc?  Is it both?

*(Bonus) **Puzzle #4.*  How would you have debugged this?  Was there 
anything I could have done to localize the problem more quickly? 



-- 
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 racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/58c6965f-a446-4812-bb04-1afb4fd7e798%40googlegroups.com.

Reply via email to